RFR 8193832: Performance of InputStream.readAllBytes() could be improved

Peter Levart peter.levart at gmail.com
Wed Dec 20 11:59:41 UTC 2017


Hi Brian,

There's also a variation of copy-ing fragment possible, that replaces 
copying with allocation:

                 byte[] copy;
                 if (nread == DEFAULT_BUFFER_SIZE) {
                     copy = buf;
                     if (n >= 0) {
                         buf = new byte[DEFAULT_BUFFER_SIZE];
                     }
                 } else {
                     copy = Arrays.copyOf(buf, nread);
                 }

For big FileInputStream(s), the buf will be fully read (nread == 
DEFAULT_BUFFER_SIZE) most of the times. So this might be an improvement 
if allocation (involving pre-zeroing) is faster than Arrays.copyOf() 
which avoids pre-zeroing, but involves copying.

Regards, Peter

On 12/20/2017 12:45 PM, Peter Levart wrote:
> Hi Brian,
>
> On 12/20/2017 12:22 AM, Brian Burkhalter wrote:
>> On Dec 19, 2017, at 2:36 PM, Brian Burkhalter 
>> <brian.burkhalter at oracle.com> wrote:
>>
>>>> You can also simplify the “for(;;) + break" into a do while loop:
>>>>
>>>> do {
>>>>   int nread = 0;
>>>>   ...
>>>> } while (n > 0);
>>> Good suggestion but I think that this needs to be "while (n >= 0)."
>> Updated version here:
>>
>> http://cr.openjdk.java.net/~bpb/8193832/webrev.02/
>>
>> Thanks,
>>
>> Brian
>
> Looks good. There is one case that could be further optimized. When 
> result.length <= DEFAULT_BUFFER_SIZE, the allocation of ArrayList 
> could be avoided. Imagine a use case where lots of small files are 
> read into byte[] arrays. For exmaple:
>
>     public byte[] readAllBytes() throws IOException {
>         List<byte[]> bufs = null;
>         byte[] result = null;
>         byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
>         int total = 0;
>         int n;
>         do {
>             int nread = 0;
>
>             // read to EOF which may read more or less than buffer size
>             while ((n = read(buf, nread, buf.length - nread)) > 0) {
>                 nread += n;
>             }
>
>             if (nread > 0) {
>                 if (MAX_BUFFER_SIZE - total < nread) {
>                     throw new OutOfMemoryError("Required array size 
> too large");
>                 }
>                 total += nread;
>                 byte[] copy = (n < 0 && nread == DEFAULT_BUFFER_SIZE) ?
>                     buf : Arrays.copyOf(buf, nread);
>                 if (result == null) {
>                     result = copy;
>                 } else {
>                     bufs = new ArrayList<>(8);
>                     bufs.add(result);
>                     bufs.add(copy);
>                 }
>             }
>         } while (n >= 0); // if the last call to read returned -1, 
> then break
>
>         if (bufs == null) {
>             return result == null ? new byte[0] : result;
>         }
>
>         result = new byte[total];
>         int offset = 0;
>         for (byte[] b : bufs) {
>             System.arraycopy(b, 0, result, offset, b.length);
>             offset += b.length;
>         }
>
>         return result;
>     }
>
>
> There is a possibility that JIT already avoids allocating ArrayList 
> utilizing EA if all involved ArrayList methods inline, so this 
> potential optimization should be tested 1st to see if it actually 
> helps improve the "small file" case.
>
> Regards, Peter
>



More information about the core-libs-dev mailing list