Skip to content

Improve performance of ClassReader.readStream()

Сергей Цыпанов requested to merge stsypanov/asm:master into master

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 in ByteArrayOutputStream

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

Merge request reports