[PATCH] optimization opportunity regarding misuse of BufferedInputStream
Сергей Цыпанов
sergei.tsypanov at yandex.ru
Thu Aug 27 20:09:28 UTC 2020
Hi,
while looking into java.util.jar.JarInputStream I've found a misuse of BufferedInputStream in checkManifest() method
where InputStream is wrapped into BIS and later read from with separately created byte[] buffer.
At runtime this means the data is copied from original IS into BIS and from there to byte[] resulting in redundant memory
allocation. The benchmark [1] demonstrates that for both scenarios (with and without verification) we can save memory
and reduce execution time by dropping BIS:
original
Mode Cnt Score Error Units
read avgt 50 230.301 ± 2.130 us/op
read:·gc.alloc.rate.norm avgt 50 148929.020 ± 22.383 B/op
readNoVerify avgt 50 228.673 ± 0.556 us/op
readNoVerify:·gc.alloc.rate.norm avgt 50 148133.555 ± 9.599 B/op
patched
Mode Cnt Score Error Units
read avgt 50 225.976 ± 0.543 us/op
read:·gc.alloc.rate.norm avgt 50 140672.404 ± 20.732 B/op
readNoVerify avgt 50 229.563 ± 1.731 us/op
readNoVerify:·gc.alloc.rate.norm avgt 50 139874.648 ± 7.054 B/op
Also InputStream.transferTo() can be called for the sake of code reuse.
Another snippets are located in MimeTable and in JmodFile:
- in MimeTable InputStream is passed into Properties.load() where they use
a buffer of exactly the same size (8192) as in BufferedInputStream. Benchmark [2]
demonstrates significant improvement when redundant bufferization is reduced:
Mode Cnt Score Error Units
buffered avgt 50 155.477 ± 6.370 us/op
buffered:·gc.alloc.rate.norm avgt 50 46263.355 ± 1.632 B/op
conventional avgt 50 146.762 ± 3.481 us/op
conventional:·gc.alloc.rate.norm avgt 50 38014.521 ± 7.152 B/op
- in JmodFile we read only the 4 first bytes but BufferedInputStream fills in the whole
buffer (by default again 8192)
Unfortunately, I couldn't construct benchmark for JmodFile, however I'm
sure there'll be improvement either.
Patch is attached. It also includes some tiny clean-ups in mentioned classes, e.g.
dead code removal
With best regards,
Sergey Tsypanov
1.
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g"})
public class ReadManifestBenchmark {
private final ClassLoader classLoader = getClass().getClassLoader();
@Benchmark
public Manifest read() throws IOException {
return readManifest(true);
}
@Benchmark
public Manifest readNoVerify() throws IOException {
return readManifest(false);
}
private Manifest readManifest(boolean verify) throws IOException {
try (
InputStream resourceAsStream = classLoader.getResourceAsStream("jmh-core-1.23.jar");
JarInputStream jarInputStream = new JarInputStream(resourceAsStream, verify)
) {
return jarInputStream.getManifest();
}
}
}
2.
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g"})
public class LoadPropertiesBenchmark {
private final ClassLoader classLoader = getClass().getClassLoader();
@Benchmark
public Object conventional() throws IOException {
try (InputStream is = getClass().getClassLoader().getResource("sun/net/www/content-types.properties").openStream()) {
Properties p = new Properties();
p.load(is);
return p;
}
}
@Benchmark
public Object buffered() throws IOException {
try (InputStream is = new BufferedInputStream(getClass().getClassLoader().getResource("sun/net/www/content-types.properties").openStream())) {
Properties p = new Properties();
p.load(is);
return p;
}
}
}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: buffered.patch
Type: text/x-diff
Size: 4110 bytes
Desc: not available
URL: <https://mail.openjdk.java.net/pipermail/core-libs-dev/attachments/20200827/0d338741/buffered.patch>
More information about the core-libs-dev
mailing list