JDK-6655735 : Integer.toString() and String.valueOf(int) contain slow delegations
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 6
  • Priority: P5
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2008-01-28
  • Updated: 2011-02-16
  • Resolved: 2009-10-09
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b74Fixed
Related Reports
Relates :  
Description
A DESCRIPTION OF THE REQUEST :
I don't understand the interest of delegating the implementation of Integer.toString() to the String class, when it can perform it directly within the class itself.

Currently: "String Integer.toString()" delegates to static "String String.valueOf(int value)" with this.value in parameter; then static "String String.valueOf(int)" redelegates back to the Integer class, using static "String Integer.toString(int value, int radix)", with the same int value in parameter, and 10 as the radix; then static "String Integer.toString(int value, int radix)" needs to test the radix to see if it's 10, and then redelegating to the other method static "String Integer.toString(int)" that performs the actual conversion.


JUSTIFICATION :
This RFE is to remove all these unnecesssary delegations (because all the involved classes are part of the same JDK core classes without any involvement of JNI optimizations), and use the most direct path.


So change this in java.lang.Integer:
    public static String toString() {
        return String.valueOf(this.value);
    }
into:
    public static String toString() {
        //return String.valueOf(this.value);
        //return Integer.valueOf(this.value, 10);
        //use the fast local static implementation directly
        return toString(this.value); // radix = 10 implied
    }
(This saves 2 delegations!)


Perform the same simplification of the code path for the "public String toString()" instance methods overriden in:
    - java.lang.Byte (saves 1 delegation):
        public String toString() {
            //return Integer.toString((int)this.value, 10);
            //use the faster static implementation directly
            return Integer.toString((int)this.value); // radix = 10 implied
       }
    - java.lang.Short (saves 1 delegation)
        public String toString() {
            //return Integer.toString((int)this.value, 10);
            //use the faster static implementation directly
            return Integer.toString((int)this.value); // radix = 10 implied
       }
but not for this overriden method in
    - java.lang.Character
which already works directly internally within the String class to create the backing char array without performing any copy (it uses a private constructor) or in:
    - java.lang.Float
    - java.lang.Double
that need a more complicate path (using FloatingDecimal for the formatting)


Change this in java.lang.String:
    public static String valueOf(int i) {
        return Integer.toString(i, 10);
    }
into directly (saves the test for radix, and subdelegation):
    // two methods are not implemented and not needed
    //    public static String valueOf(byte b)
    //    public static String valueOf(short s)
    // as the callers use basic integer promotion to this method
    public static String valueOf(int i) {
        //return Integer.toString(i, 10);
        //use the faster static implementation directly
        return Integer.toString(i); // radix = 10 implied
    }
(This saves 1 delegations)


Perform the same thing for the following static methods in java.lang.String:
    public static String valueOf(long l) {
        //return Long.toString(l, 10);
        return Long.toString(l); // radix=10 implied
    }
but don't change:
    public static String valueOf(float f) {
        return Float.toString(f);
        //currently implemented internally as:
        //return new FloatingDecimal(f).toJavaFormatString();
    }
    public static String valueOf(double d) {
        return Double.toString(d);
        //currently implemented internally as:
        //return new FloatingDecimal(d).toJavaFormatString();
    }
And don't change:
   public static String valueOf(char value) {
       return String.valueOf(value);
   }
which delegates to the String class to work faster with a internal private constructor (to void the copy of a char array for the backing store)


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Behavior is correct, just unnecessarily slow.
ACTUAL -
Currently does not use direct paths for documented methods in the same core java.lang package of the JRE. So this creates branching, additional stack frames, that even the JIT compiler will not easily simplify by inlining.

The toString() methods for boxed native objects are very frequently used in the JDK (for example with the Collections framework that cannot store native objects in native arrays, but still use boxed object arrays.)

----
Note that the standard Collections should be optimized to use native arrays if possible, for example class HashMap<K,V> should be able to optimize HashMap<Integer,?> or HashMap<?,Integer>, by providing a constructor taking class arguments, and then delegating to specialized derived subclasses that use native arrays for their internal backing store. The problem is that it declares the internal array of Map.Entry by implementing it locally as HashMap.Entry with a concrete implementation where K and V are necessarily boxing objects.

For this reason, many applications need to avoid HashMap<K,V> and will instead rewrite:
* HashMapFromInteger<V> by extending class AbstractHashMap<K,V> (and HashMapFromInteger<V>.Entry by implementing interface Map.Entry<K, Integer>)
* HashMapToInteger<K> by extending class AbstractHashMap<K,Integer> (and HashMapFromInteger<V>.Entry from interface Map.Entry<Integer, V>)
* HashMapIntegerToInteger by extending one or the other, but with this approach, it is impossible to make it compatible with the two others.

It should be possible to declare specifializations to native type in Java, but type erasure only allows Object as the maximum bound for <?> (Object itself cannot be perceived as a specialization extending an abstract class Type, just like native types would specialize and extend the same abstract class Type).

  To make this possible, it should be possible to write abstract-only classes or interfaces using parameters like <T extends Type>, instead of <T> (bound to Object), and concrete implementations with <byte>, <int>, ... and <Object> (or <? extends Object). This should also be made coherent with Java 1.5+ "autoboxing" of native types: autoboxing of native types to Object types for type paramers of classes should only be performed by the Java compiler if there's no declared or imported specialization for the native type, just like it happens when invoking overloaded methods declared with either (int) or (Integer) parameters.

But this would require lots of change on the JVM, because this would require supporting abstract  native types in the bytecode generated by the Java compiler, and have it supported at runtime so that the compiled code works like C/C++ templates (except that the VM would then be able to instanciate the concrete native classes or generic Object-based classes at runtime, including the dynamic generation and optimization of the concrete bytecode, if there's no accessible concrete implementation of a specialization to Generic or native type).

For now the alternative is to use Integer, Byte, ... and autoboxing, and making sure that the VM performs with good performance with temporarily boxed objects (but unfortunately, the boxed objects have no final values, only final references, so they remain mutable, and not all optimizations are possible, instead it highly depends on the performance of the allocator and garbage collector for handling many autoboxed native objects autounboxed objects, when needed to get the final/immutable semantic such as the constants needed in interfaces).

Comments
SUGGESTED FIX See suggested fix of 6480728.
07-10-2009

PUBLIC COMMENTS See http://hg.openjdk.java.net/jdk7/tl/jdk/rev/e7ad502130ba
07-10-2009

EVALUATION A fine idea.
07-10-2009