Improve performance of ClassReader.readStream()
Hello, ClassReader.readStream()
can be reworked to improve it's performance and reduce memory consumption. Currently there are two major problems about it:
- we always allocate a buffer of the same size which is often several times bigger than actual amount of data being read from
InputStream
- we always return of copy of
byte[]
underlying inByteArrayOutputStream
Using InputStream.available()
we can presize the buffer in vast majority of cases and even when the whole data is not read within one iteration (for large InputStreams
) we allocate as much memory as required. Another optimization is about checking whether it was only one read from InputStream
and in this case the buffer itself can be returned instead of calling ByteArrayOutputStream.toByteArray()
.
I've used this benchmark available here to check impact of the patch for start-up of simple Spring Boot application (Spring Boot sources contains repackaged fork of asm):
@State(Scope.Thread)
@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g"})
public class SpringBootApplicationBenchmark {
private ConfigurableApplicationContext context;
@Benchmark
public Object startUp() {
return context = SpringApplication.run(BenchmarkApplication.class);
}
@TearDown(Level.Invocation)
public void close() {
context.close();
}
}
and got the following results:
original
Benchmark Mode Cnt Score Error Units
SpringBootApplicationBenchmark.startUp ss 200 718.760 ± 20.549 ms/op
SpringBootApplicationBenchmark.startUp:·gc.alloc.rate ss 200 38.895 ± 0.620 MB/sec
SpringBootApplicationBenchmark.startUp:·gc.alloc.rate.norm ss 200 49776228.720 ± 19919.815 B/op
SpringBootApplicationBenchmark.startUp:·gc.count ss 200 ≈ 0 counts
patched
Benchmark Mode Cnt Score Error Units
SpringBootApplicationBenchmark.startUp ss 200 614.820 ± 7.031 ms/op
SpringBootApplicationBenchmark.startUp:·gc.alloc.rate ss 200 41.678 ± 0.240 MB/sec
SpringBootApplicationBenchmark.startUp:·gc.alloc.rate.norm ss 200 49003403.920 ± 21786.877 B/op
SpringBootApplicationBenchmark.startUp:·gc.count ss 200 ≈ 0 counts