[NEW BUG] Reduce allocations from Class.toString() for primitives
Claes Redestad
claes.redestad at oracle.com
Mon Apr 6 18:31:47 UTC 2020
Hi Christoph,
while the patch seems ok, I fail to think of a scenario where
performance of Class.toString() might be critical. Also it seems this
code is subject to change via valhalla[1]. So I think I'll say no, for
once. :-)
Thanks!
/Claes
[1]
https://github.com/openjdk/valhalla/blob/lworld/src/java.base/share/classes/java/lang/Class.java#L197
On 2020-04-06 19:49, Christoph Dreis wrote:
> Hi,
>
> I just noticed an opportunity to optimize Class.toString() for primitive types.
>
> I've written a small benchmark and ran it against JDK 15 vs a patched variant.
>
> @BenchmarkMode(Mode.AverageTime)
> @OutputTimeUnit(TimeUnit.NANOSECONDS)
> public class MyBenchmark {
>
> @State(Scope.Benchmark)
> public static class ThreadState {
>
> private Class<?> primitive = long.class;
> private Class<?> clazz = Long.class;
> private Class<?> interfaze = Map.class;
>
> }
>
> @Benchmark
> public String testPrimitive(ThreadState threadState) {
> return threadState.primitive.toString();
> }
>
> @Benchmark
> public String testClass(ThreadState threadState) {
> return threadState.clazz.toString();
> }
>
> @Benchmark
> public String testInterface(ThreadState threadState) {
> return threadState.interfaze.toString();
> }
>
> }
>
> This yields the following results:
>
> Before
> Benchmark Mode Cnt Score Error Units
> MyBenchmark.testClass avgt 10 32,268 ± 2,961 ns/op
> MyBenchmark.testClass:·gc.alloc.rate avgt 10 3601,964 ± 336,786 MB/sec
> MyBenchmark.testClass:·gc.alloc.rate.norm avgt 10 152,009 ± 0,001 B/op
> MyBenchmark.testClass:·gc.count avgt 10 203,000 counts
> MyBenchmark.testClass:·gc.time avgt 10 138,000 ms
> MyBenchmark.testInterface avgt 10 37,685 ± 3,728 ns/op
> MyBenchmark.testInterface:·gc.alloc.rate avgt 10 3086,794 ± 309,983 MB/sec
> MyBenchmark.testInterface:·gc.alloc.rate.norm avgt 10 152,008 ± 0,001 B/op
> MyBenchmark.testInterface:·gc.count avgt 10 160,000 counts
> MyBenchmark.testInterface:·gc.time avgt 10 107,000 ms
> MyBenchmark.testPrimitive avgt 10 20,937 ± 2,668 ns/op
> MyBenchmark.testPrimitive:·gc.alloc.rate avgt 10 3809,437 ± 470,140 MB/sec
> MyBenchmark.testPrimitive:·gc.alloc.rate.norm avgt 10 104,006 ± 0,001 B/op
> MyBenchmark.testPrimitive:·gc.count avgt 10 215,000 counts
> MyBenchmark.testPrimitive:·gc.time avgt 10 167,000 ms
>
>
> After
> Benchmark Mode Cnt Score Error Units
> MyBenchmark.testClass avgt 10 31,585 ± 5,365 ns/op
> MyBenchmark.testClass:·gc.alloc.rate avgt 10 3704,714 ± 549,224 MB/sec
> MyBenchmark.testClass:·gc.alloc.rate.norm avgt 10 152,008 ± 0,001 B/op
> MyBenchmark.testClass:·gc.count avgt 10 191,000 counts
> MyBenchmark.testClass:·gc.time avgt 10 139,000 ms
> MyBenchmark.testInterface avgt 10 34,534 ± 2,073 ns/op
> MyBenchmark.testInterface:·gc.alloc.rate avgt 10 3358,401 ± 193,391 MB/sec
> MyBenchmark.testInterface:·gc.alloc.rate.norm avgt 10 152,008 ± 0,001 B/op
> MyBenchmark.testInterface:·gc.count avgt 10 173,000 counts
> MyBenchmark.testInterface:·gc.time avgt 10 131,000 ms
> MyBenchmark.testPrimitive avgt 10 2,829 ± 0,139 ns/op
> MyBenchmark.testPrimitive:·gc.alloc.rate avgt 10 ≈ 10⁻⁴ MB/sec
> MyBenchmark.testPrimitive:·gc.alloc.rate.norm avgt 10 ≈ 10⁻⁶ B/op
> MyBenchmark.testPrimitive:·gc.count avgt 10 ≈ 0 counts
>
> I don't think the patched version is actually faster for classes & interfaces; I guess it's rather a draw for those.
> Yet, for primitives we can see a 10x improvement due to the avoided string concats.
>
> In case you think this is worthwhile I would need someone to sponsor this patch.
> I would highly appreciate that. Let me know what you think
>
> Cheers,
> Christoph
>
>
> ===== PATCH =====
> diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java
> --- a/src/java.base/share/classes/java/lang/Class.java
> +++ b/src/java.base/share/classes/java/lang/Class.java
> @@ -196,8 +196,11 @@
> * @return a string representation of this {@code Class} object.
> */
> public String toString() {
> - return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
> - + getName();
> + String name = getName();
> + if (isPrimitive()) {
> + return name;
> + }
> + return (isInterface() ? "interface " : "class ") + name;
> }
>
>
More information about the core-libs-dev
mailing list