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;
     }


Reply via email to