commons-io icon indicating copy to clipboard operation
commons-io copied to clipboard

IO-649 - Improve the performance of the contentEquals() methods.

Open brettlounsbury opened this issue 6 years ago • 16 comments

This change modifies the contentEquals() methods to internally buffer content into a byte/char array and to then do batch comparisons of those arrays using Arrays.equals instead of using a BufferedInputStream or BufferedReader and making use of the single byte/char read() methods.

This reduces the number of method invocations by a factor equal to the buffer size and avoids casting every byte read to an int and improves performance significantly.

The following table shows the performance increase over 1000 iterations of comparing 2 1GB InputStream of binary data (stored in memory to avoid I/O). This test was performed on an EC2 M4.4XL host using Java 1.8.0.232 and there was a forced System.gc() between each iteration to avoid GC as a source of latency:

Average: 7236 to 858ms (8.43x speedup) P50: 7224 to 856ms (8.44x speedup) P90: 7249 to 860ms (8.43x speedup) P99: 7410 to 913ms (8.12x speedup) P100: 8330 to 1278ms (6.52x speedup)

The following table shows the performance increase over 1000 iterations of comparing 2 1GB Reader of character data (stored in memory to avoid I/O). This test was performed on an EC2 M4.4XL host using Java 1.8.0.232 and there was a forced System.gc() between each iteration to avoid GC as a source of latency:

Average: 11281 to 1737ms (6.50x speedup) P50: 11262 to 1735ms (6.49x speedup) P90: 11292 to 1741ms (6.49x speedup) P99: 11707 to 1774ms (6.60x speedup) P100: 12176 to 1884ms (6.46x speedup)

brettlounsbury avatar Jan 13 '20 17:01 brettlounsbury

https://issues.apache.org/jira/browse/IO-649

brettlounsbury avatar Jan 13 '20 17:01 brettlounsbury

Coverage Status

Coverage increased (+0.06%) to 89.837% when pulling 2a55f16dce4476bbeae57739fd03421cf4edc970 on brettlounsbury:master into 133fb1717443f3110d812dd65a5238a790ce39b1 on apache:master.

coveralls avatar Jan 13 '20 19:01 coveralls

I took a look at the coverage drop. I forgot to add tests that exercise the bounds checking of the bufferSize inputs to both contentEquals method. I've pushed a new version with that.

brettlounsbury avatar Jan 13 '20 20:01 brettlounsbury

@garydgregory - can you take a look or propose another reviewer?

brettlounsbury avatar Jan 24 '20 15:01 brettlounsbury

Hi @brettlounsbury.

Thank you for your PR and updates. I've sprinkled comments throughout.

Performance claims must be backed up somehow, please add a JMH benchmark to the test so myself and others can compare mileage.

For examples of JMH usage, please see other Apache Commons components like Commons BCEL, Commons Crypto, Commons CSV, Commons Lang, Commons Math, and Commons RNG.

Thank you! Gary

garydgregory avatar Jan 24 '20 18:01 garydgregory

Hi @garydgregory,

I don't see any comments from you other than the request for a benchmark.

I have updated IOUtils to include a test dependency on JMH and and to include a benchmark that shows BufferedInputStream.read() performance compared to InputStream.read(byte[]) performance. This test creates an 8MB file on disk once before the benchmark is ran and reads it back in. It then just keeps a running sum of the bytes in the file (and ignores any overflow/underflow) and blackhole's the sum at the end to avoid any compiler optimization.

Results look something like this (Multi-byte reads are about 3.5x faster): Benchmark Mode Cnt Score Error Units SingleByteReadVsMultiByteReadPerformanceTest.testReadMultiBytes avgt 25 6220618.290 ± 204562.812 ns/op SingleByteReadVsMultiByteReadPerformanceTest.testReadSingleByte avgt 25 21740409.146 ± 711294.924 ns/op

brettlounsbury avatar Jan 27 '20 14:01 brettlounsbury

@garydgregory,

I really appreciate your thorough review of my code. I think at this point I have addressed your comments, but if you have any others please let me know.

I will add that I made the IOUtils.DEFAULT_BUFFER_SIZE constant package private (default visibility) so it could be referenced from IOUtils tests so that I can ensure the logic makes sense around buffer boundaries and I didn't want to clone that property since it could later be mutated and invalidate some of the edge condition testing being done.

brettlounsbury avatar Jan 27 '20 15:01 brettlounsbury

@garydgregory - Any other feedback here or anything else I can do to improve this?

brettlounsbury avatar Feb 06 '20 04:02 brettlounsbury

@garydgregory - Can you take another look or suggest another reviewer?

brettlounsbury avatar Feb 20 '20 17:02 brettlounsbury

Sorry about the delay, last day of vacation here ;-)

garydgregory avatar Mar 02 '20 19:03 garydgregory

Sorry about the delay, last day of vacation here ;-)

No worries - hope you had a great vacation!

brettlounsbury avatar Mar 02 '20 20:03 brettlounsbury

@garydgregory - can you take another look when you have a chance.

brettlounsbury avatar Mar 19 '20 23:03 brettlounsbury

@garydgregory - I think all necessary changes have been made. Are you able to take a look?

brettlounsbury avatar Apr 24 '20 21:04 brettlounsbury

@brettlounsbury May you please rebase on master?

garydgregory avatar Apr 26 '20 15:04 garydgregory

@garydgregory - resolved the merge conflict, should be good now.

brettlounsbury avatar May 18 '20 18:05 brettlounsbury

@brettlounsbury Please rebase on master and keep your changes to a minimum, the current change set is for 53 files! Also compare with https://issues.apache.org/jira/projects/IO/issues/IO-670

garydgregory avatar Jan 21 '21 15:01 garydgregory