On Thu, 22 Apr 2021 14:07:20 GMT, Сергей Цыпанов <github.com+10835776+stsypa...@openjdk.org> wrote:
> Hello, from discussion in https://github.com/openjdk/jdk/pull/3464 I've found > out, that in a few of JDK core classes, e.g. in `j.l.Class` expressions like > `baseName.replace('.', '/') + '/' + name` are not compiled into > `invokedynamic`-based code, but into one using `StringBuilder`. This happens > due to some bootstraping issues. > > However the code like > > private String getSimpleName0() { > if (isArray()) { > return getComponentType().getSimpleName() + "[]"; > } > //... > } > > can be improved via replacement of `+` with `String.concat()` call. > > I've used this benchmark to measure impact: > > @State(Scope.Thread) > @BenchmarkMode(Mode.AverageTime) > @OutputTimeUnit(TimeUnit.NANOSECONDS) > @Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g"}) > public class ClassMethodsBenchmark { > private final Class<? extends Object[]> arrayClass = Object[].class; > private Method descriptorString; > > @Setup > public void setUp() throws NoSuchMethodException { > //fore some reason compiler doesn't allow me to call > Class.descriptorString() directly > descriptorString = Class.class.getDeclaredMethod("descriptorString"); > } > > @Benchmark > public Object descriptorString() throws Exception { > return descriptorString.invoke(arrayClass); > } > > @Benchmark > public Object typeName() { > return arrayClass.getTypeName(); > } > } > > and got those results > > Mode Cnt Score > Error Units > descriptorString avgt 60 91.480 ± > 1.839 ns/op > descriptorString:·gc.alloc.rate.norm avgt 60 404.008 ± > 4.033 B/op > descriptorString:·gc.churn.G1_Eden_Space avgt 60 2791.866 ± > 181.589 MB/sec > descriptorString:·gc.churn.G1_Eden_Space.norm avgt 60 401.702 ± > 26.047 B/op > descriptorString:·gc.churn.G1_Survivor_Space avgt 60 0.003 ± > 0.001 MB/sec > descriptorString:·gc.churn.G1_Survivor_Space.norm avgt 60 ≈ 10⁻³ > B/op > descriptorString:·gc.count avgt 60 205.000 > counts > descriptorString:·gc.time avgt 60 216.000 > ms > > patched > Mode Cnt Score > Error Units > descriptorString avgt 60 65.016 ± > 3.446 ns/op > descriptorString:·gc.alloc.rate avgt 60 2844.240 ± > 115.719 MB/sec > descriptorString:·gc.alloc.rate.norm avgt 60 288.006 ± > 0.001 B/op > descriptorString:·gc.churn.G1_Eden_Space avgt 60 2832.996 ± > 206.939 MB/sec > descriptorString:·gc.churn.G1_Eden_Space.norm avgt 60 286.955 ± > 17.853 B/op > descriptorString:·gc.churn.G1_Survivor_Space avgt 60 0.003 ± > 0.001 MB/sec > descriptorString:·gc.churn.G1_Survivor_Space.norm avgt 60 ≈ 10⁻³ > B/op > descriptorString:·gc.count avgt 60 208.000 > counts > descriptorString:·gc.time avgt 60 228.000 > ms > ----------------- > typeName avgt 60 34.273 ± > 0.480 ns/op > typeName:·gc.alloc.rate avgt 60 3265.356 ± > 45.113 MB/sec > typeName:·gc.alloc.rate.norm avgt 60 176.004 ± > 0.001 B/op > typeName:·gc.churn.G1_Eden_Space avgt 60 3268.454 ± > 134.458 MB/sec > typeName:·gc.churn.G1_Eden_Space.norm avgt 60 176.109 ± > 6.595 B/op > typeName:·gc.churn.G1_Survivor_Space avgt 60 0.003 ± > 0.001 MB/sec > typeName:·gc.churn.G1_Survivor_Space.norm avgt 60 ≈ 10⁻⁴ > B/op > typeName:·gc.count avgt 60 240.000 > counts > typeName:·gc.time avgt 60 255.000 > ms > > patched > > typeName avgt 60 15.787 ± > 0.214 ns/op > typeName:·gc.alloc.rate avgt 60 2577.554 ± > 32.339 MB/sec > typeName:·gc.alloc.rate.norm avgt 60 64.001 ± > 0.001 B/op > typeName:·gc.churn.G1_Eden_Space avgt 60 2574.039 ± > 147.774 MB/sec > typeName:·gc.churn.G1_Eden_Space.norm avgt 60 63.895 ± > 3.511 B/op > typeName:·gc.churn.G1_Survivor_Space avgt 60 0.003 ± > 0.001 MB/sec > typeName:·gc.churn.G1_Survivor_Space.norm avgt 60 ≈ 10⁻⁴ > B/op > typeName:·gc.count avgt 60 189.000 > counts > typeName:·gc.time avgt 60 199.000 > ms > > I think this patch is likely to improve reflection operations related to > arrays. > > If one finds this patch useful, then I'll create a ticket to track this. Also > it'd be nice to have a list of classes, that are compiled in the same way as > `j.l.Class` (i.e. have no access to `invokedynamic`) so I could look into > them for other snippets where two String are joined so `concat`-based > optimization is possible. > > Pre-sizing of `StringBuilder` for `Class.gdescriptorString()` and > `Class.getCanonicalName0()` is one more improvement opportunity here. Spotted one more method to be improved: @State(Scope.Benchmark) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) @Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g"}) public class ClassToStringBenchmark { private final Class<?> clazzWithShortName = Object.class; private final Class<?> interfaceWithShortName = Serializable.class; private final Class<?> primitive = int.class; @Benchmark public String classToString() { return clazzWithShortName.toString(); } @Benchmark public String interfaceToString() { return interfaceWithShortName.toString(); } @Benchmark public String primitiveToString() { return primitive.toString(); } } giving before Mode Cnt Score Error Units classToString avgt 50 30,769 ± 0,352 ns/op classToString:·gc.alloc.rate avgt 50 3769,212 ± 42,959 MB/sec classToString:·gc.alloc.rate.norm avgt 50 152,003 ± 0,001 B/op classToString:·gc.churn.G1_Eden_Space avgt 50 3776,990 ± 112,309 MB/sec classToString:·gc.churn.G1_Eden_Space.norm avgt 50 152,328 ± 4,317 B/op classToString:·gc.churn.G1_Survivor_Space avgt 50 0,004 ± 0,001 MB/sec classToString:·gc.churn.G1_Survivor_Space.norm avgt 50 ≈ 10⁻⁴ B/op classToString:·gc.count avgt 50 385,000 counts classToString:·gc.time avgt 50 378,000 ms interfaceToString avgt 50 35,571 ± 0,583 ns/op interfaceToString:·gc.alloc.rate avgt 50 3433,504 ± 52,056 MB/sec interfaceToString:·gc.alloc.rate.norm avgt 50 160,003 ± 0,001 B/op interfaceToString:·gc.churn.G1_Eden_Space avgt 50 3443,384 ± 91,647 MB/sec interfaceToString:·gc.churn.G1_Eden_Space.norm avgt 50 160,456 ± 3,454 B/op interfaceToString:·gc.churn.G1_Survivor_Space avgt 50 0,004 ± 0,001 MB/sec interfaceToString:·gc.churn.G1_Survivor_Space.norm avgt 50 ≈ 10⁻⁴ B/op interfaceToString:·gc.count avgt 50 351,000 counts interfaceToString:·gc.time avgt 50 351,000 ms primitiveToString avgt 50 20,193 ± 0,369 ns/op primitiveToString:·gc.alloc.rate avgt 50 3932,425 ± 68,453 MB/sec primitiveToString:·gc.alloc.rate.norm avgt 50 104,002 ± 0,001 B/op primitiveToString:·gc.churn.G1_Eden_Space avgt 50 3943,662 ± 119,781 MB/sec primitiveToString:·gc.churn.G1_Eden_Space.norm avgt 50 104,305 ± 2,678 B/op primitiveToString:·gc.churn.G1_Survivor_Space avgt 50 0,005 ± 0,001 MB/sec primitiveToString:·gc.churn.G1_Survivor_Space.norm avgt 50 ≈ 10⁻⁴ B/op primitiveToString:·gc.count avgt 50 402,000 counts primitiveToString:·gc.time avgt 50 387,000 ms after Mode Cnt Score Error Units classToString avgt 50 13,552 ± 0,114 ns/op classToString:·gc.alloc.rate avgt 50 3602,305 ± 27,881 MB/sec classToString:·gc.alloc.rate.norm avgt 50 64,001 ± 0,001 B/op classToString:·gc.churn.G1_Eden_Space avgt 50 3619,864 ± 119,002 MB/sec classToString:·gc.churn.G1_Eden_Space.norm avgt 50 64,318 ± 2,081 B/op classToString:·gc.churn.G1_Survivor_Space avgt 50 0,004 ± 0,001 MB/sec classToString:·gc.churn.G1_Survivor_Space.norm avgt 50 ≈ 10⁻⁴ B/op classToString:·gc.count avgt 50 369,000 counts classToString:·gc.time avgt 50 366,000 ms interfaceToString avgt 50 16,480 ± 0,113 ns/op interfaceToString:·gc.alloc.rate avgt 50 3332,045 ± 21,628 MB/sec interfaceToString:·gc.alloc.rate.norm avgt 50 72,001 ± 0,001 B/op interfaceToString:·gc.churn.G1_Eden_Space avgt 50 3335,296 ± 98,091 MB/sec interfaceToString:·gc.churn.G1_Eden_Space.norm avgt 50 72,086 ± 2,187 B/op interfaceToString:·gc.churn.G1_Survivor_Space avgt 50 0,004 ± 0,001 MB/sec interfaceToString:·gc.churn.G1_Survivor_Space.norm avgt 50 ≈ 10⁻⁴ B/op interfaceToString:·gc.count avgt 50 340,000 counts interfaceToString:·gc.time avgt 50 337,000 ms primitiveToString avgt 50 5,992 ± 0,079 ns/op primitiveToString:·gc.alloc.rate avgt 50 3056,666 ± 39,390 MB/sec primitiveToString:·gc.alloc.rate.norm avgt 50 24,000 ± 0,001 B/op primitiveToString:·gc.churn.G1_Eden_Space avgt 50 3070,543 ± 107,567 MB/sec primitiveToString:·gc.churn.G1_Eden_Space.norm avgt 50 24,114 ± 0,814 B/op primitiveToString:·gc.churn.G1_Survivor_Space avgt 50 0,004 ± 0,001 MB/sec primitiveToString:·gc.churn.G1_Survivor_Space.norm avgt 50 ≈ 10⁻⁵ B/op primitiveToString:·gc.count avgt 50 313,000 counts primitiveToString:·gc.time avgt 50 310,000 ms ------------- PR: https://git.openjdk.java.net/jdk/pull/3627