RFR: 6478546: FileInputStream.read() throws OutOfMemoryError when there is plenty available [v2]

Brian Burkhalter bpb at openjdk.java.net
Thu May 5 23:39:31 UTC 2022


On Thu, 28 Apr 2022 20:02:36 GMT, Brian Burkhalter <bpb at openjdk.org> wrote:

>> Modify native multi-byte read-write code used by the `java.io` classes to limit the size of the allocated native buffer thereby decreasing off-heap memory footprint and increasing throughput.
>
> Brian Burkhalter has updated the pull request incrementally with one additional commit since the last revision:
> 
>   6478546: Decrease malloc'ed buffer maximum size to 64 kB

Further performance testing was conducted for the case where the native read and write functions used a fixed, stack-allocated buffer of size 8192. The loops were moved up into the Java code of `FileInputStream`, `FileOutputStream` and `RandomAccessFile`. Note that there was code duplication because RAF needs both read and write methods as well. The performance of writing with this approach was approximately half what it had been, so for writing the approach was abandoned.

Here are some updated performance measurements:

<img width="721" alt="FileInputStream-read-perf" src="https://user-images.githubusercontent.com/71468245/167041493-6d4c421c-c2ec-4a8a-8b32-09b2a902a77c.png">

<img width="720" alt="FileOutputStream-write-perf" src="https://user-images.githubusercontent.com/71468245/167041541-94e5806c-de86-4e62-a117-4cfafac82e87.png">

The performance measurements shown are for the following cases:

1. Master: unmodified code as it exists in the mainline
2. Java: fixed-size stack buffer in native read, read loops in Java, write as in the mainline but with malloc buffer size limit
3. Native: read loop in native read with malloc buffer size limit, write as in the mainline but with malloc buffer size limit

The horizontal axis represents a variety of lengths from 8192 to 1GB; the vertical axis is throughput (ops/s) on a log 10 scale. The native lines in the charts are for the code proposed to be integrated.

As can be seen, the performance of reading is quite similar up to larger lengths. The mainline version presumably starts to suffer the effect of large allocations. The native read loop performs the best throughout, being for lengths 10 MB and above from 50% to 3X faster than the mainline version. The native read loop is about 40% faster than the Java read loop for these larger lengths.

Due to the log scale of the charts, the reading performance detail cannot be seen exactly and so is given here for the larger lengths:


               Throughput of read(byte[]) (ops/s)
   Length      Master         Java        Native
   1048576    11341.39      6124.482    11371.091
  10485760      356.893      376.326      557.906
 251503002       10.036       14.27        19.869
 524288000        5.005        6.857        9.552
1000000000        1.675        3.527        4.997

The performance of writing is about the same for the Java and Native versions, as it should be since the implementations are the same. Any difference is likely due to measurement noise. The mainline version again suffers for larger lengths.

As the native write loop was already present in the mainline code, the principal complexity proposed to be added is the native read loop. Given the improved throughput and vastly reduced native memory allocation this seems to be justified.

-------------

PR: https://git.openjdk.java.net/jdk/pull/8235


More information about the core-libs-dev mailing list