Hello, I'd like to contribute this trivial patch to JDK.

The idea behind it is that with compact strings we don't need char[] when all 
symbols are ASCII.

This allows to slightly reduce footprint of java.lang.Integer.class and drop 
casts from char to byte.

I've measured performance of patched code using Integer/Long.toString() and 
toHexString() methods for short and long values
and the patch is likely to improve it slightly, at least for Long:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g", "-XX:+UseParallelGC"})
public class IntegerToString {
  @Benchmark
  public String integerToHexString(Data data) { return 
Integer.toHexString(data.value); }

  @Benchmark
  public String integerToString(Data data) { return 
Integer.toString(data.value); }

  @State(Scope.Thread)
  public static class Data {
    @Param({"0", "2147483647"}) private int value;
  }
}


Gives

Benchmark                                       (value)  Mode  Cnt   Score   
Error  Units
IntegerToString.integerToHexString                    0  avgt   50   7.833 ± 
0.172  ns/op
IntegerToString.integerToString                       0  avgt   50   7.550 ± 
0.070  ns/op
IntegerToString.integerToHexString           2147483647  avgt   50  13.200 ± 
0.268  ns/op
IntegerToString.integerToString              2147483647  avgt   50  17.028 ± 
0.572  ns/op

after

Benchmark                                       (value)  Mode  Cnt   Score   
Error  Units
IntegerToString.integerToHexString                    0  avgt   50   7.771 ± 
0.410  ns/op
IntegerToString.integerToString                       0  avgt   50   7.779 ± 
0.321  ns/op
IntegerToString.integerToHexString           2147483647  avgt   50  13.193 ± 
0.450  ns/op
IntegerToString.integerToString              2147483647  avgt   50  16.641 ± 
0.332  ns/op

As of Long the corresponding benchmark

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g", "-XX:+UseParallelGC"})
public class LongToString {
  @Benchmark
  public String longToHexString(Data data) { return 
Long.toHexString(data.longValue); }

  @Benchmark
  public String longToString(Data data) { return Long.toString(data.longValue); 
}

  @State(Scope.Thread)
  public static class Data {
    @Param({"0", "9223372036854775807"}) private long longValue;
  }
}


gives

before

LongToString.longToHexString                          0  avgt   50   7.711 ± 
0.303  ns/op
LongToString.longToString                             0  avgt   50   7.321 ± 
0.439  ns/op
LongToString.longToHexString        9223372036854775807  avgt   50  17.393 ± 
0.515  ns/op
LongToString.longToString           9223372036854775807  avgt   50  30.192 ± 
1.365  ns/op

after

LongToString.longToHexString                          0  avgt   50   7.796 ± 
0.250  ns/op
LongToString.longToString                             0  avgt   50   6.497 ± 
0.015  ns/op
LongToString.longToHexString        9223372036854775807  avgt   50  17.720 ± 
0.914  ns/op
LongToString.longToString           9223372036854775807  avgt   50  27.089 ± 
0.232  ns/op

Also I've included into that patch trivial change for Integer.IntegerCache 
where we can use local variable size
instead of c.length where c is array. This is likely to slightly improve 
startup time as this code is executed at class
loading time and likely to be executed in interpreter mode rather than 
beingcompiled by optimizing compiler.

Regards,
Sergey Tsypanov
diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java
--- a/src/java.base/share/classes/java/lang/Integer.java
+++ b/src/java.base/share/classes/java/lang/Integer.java
@@ -87,7 +87,7 @@
     /**
      * All possible chars for representing a number as a String
      */
-    static final char[] digits = {
+    static final byte[] digits = {
         '0' , '1' , '2' , '3' , '4' , '5' ,
         '6' , '7' , '8' , '9' , 'a' , 'b' ,
         'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
@@ -159,10 +159,10 @@
             }
 
             while (i <= -radix) {
-                buf[charPos--] = (byte)digits[-(i % radix)];
+                buf[charPos--] = digits[-(i % radix)];
                 i = i / radix;
             }
-            buf[charPos] = (byte)digits[-i];
+            buf[charPos] = digits[-i];
 
             if (negative) {
                 buf[--charPos] = '-';
@@ -371,7 +371,7 @@
         int radix = 1 << shift;
         int mask = radix - 1;
         do {
-            buf[--charPos] = (byte)Integer.digits[val & mask];
+            buf[--charPos] = Integer.digits[val & mask];
             val >>>= shift;
         } while (charPos > 0);
     }
@@ -1030,7 +1030,7 @@
             if (archivedCache == null || size > archivedCache.length) {
                 Integer[] c = new Integer[size];
                 int j = low;
-                for(int i = 0; i < c.length; i++) {
+                for(int i = 0; i < size; i++) {
                     c[i] = new Integer(j++);
                 }
                 archivedCache = c;
diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java
--- a/src/java.base/share/classes/java/lang/Long.java
+++ b/src/java.base/share/classes/java/lang/Long.java
@@ -145,10 +145,10 @@
             }
 
             while (i <= -radix) {
-                buf[charPos--] = (byte)Integer.digits[(int)(-(i % radix))];
+                buf[charPos--] = Integer.digits[(int)(-(i % radix))];
                 i = i / radix;
             }
-            buf[charPos] = (byte)Integer.digits[(int)(-i)];
+            buf[charPos] = Integer.digits[(int)(-i)];
 
             if (negative) {
                 buf[--charPos] = '-';
@@ -413,7 +413,7 @@
         int radix = 1 << shift;
         int mask = radix - 1;
         do {
-            buf[--charPos] = (byte)Integer.digits[((int) val) & mask];
+            buf[--charPos] = Integer.digits[((int) val) & mask];
             val >>>= shift;
         } while (charPos > offset);
     }

Reply via email to