-
Enhancement
-
Resolution: Unresolved
-
P4
-
None
-
8u11
-
generic
-
generic
A DESCRIPTION OF THE REQUEST :
The get/set/getLength methods of java.lang.reflect.Array are very slow. The existing methods are native methods, and in the C source file sits a lonely "TODO: Performance" comment which seems to have been there for at least a decade, and maybe longer.
I've written a simple re-implementation of the methods in pure Java code, pasted below (released into public domain or whatever license you would need if you wanted to use it). These methods are exactly compatible in the conversions they allow and don't allow and the types of exceptions they throw.
The pure Java methods are faster. They are up to five times faster on the HotSpot Client VM and up to a hundred times faster on the HotSpot Server VM. I do know how to microbenchmark properly on a JIT. The reason for the massive difference is because the existing implementation really is that atrocious.
It's not too surprising that the Java implementation is competitive. The methods are fairly short and straightforward, so modern HotSpot can implement them about as well as C can, plus has all the advantages of aggressive runtime inlining that C can never do, and without the overhead of the transition between Java and native code.
The getLength method is the only potential surprise: whereas the native implementation can presumably read the length from the array object header regardless of its type, the Java implementation needs to cast it to the correct array type since the length field is not part of a common superclass. What's surprising is that the pure Java getLength method is faster despite that. I suppose the cost of the transition between Java and native code is what cripples the performance of the native version.
JUSTIFICATION :
1. Performance. The magnitude of the improvement is great.
2. In general, increasing the amount of the Java platform that is written in Java rather than C increases the maintainability and portability of the platform (and is a minor point of language pride).
CAVEAT :
If value types (and corresponding arrays of value types) are implemented in a future version of the Java language, it's unclear whether reflection of such arrays would be better implemented in Java or in C.
SOURCE :
public class Array {
/*
* Legal conversions:
* From \ To| boolean | byte | char | short | int | long | float | double
* ----------|-------------------------------------------------------------
* boolean | #
* byte | # # # # # #
* char | # # # # #
* short | # # # # #
* int | # # # #
* long | # # #
* float | # #
* double | #
*/
private static RuntimeException badArray(Object array) {
if (array == null)
return new NullPointerException("Array argument is null");
else if (!array.getClass().isArray())
return new IllegalArgumentException("Argument is not an array");
else
return new IllegalArgumentException("Array is of incompatible type");
}
public static int getLength(Object array) {
// Note: all types of multidimensional arrays are instanceof Object[]
if (array instanceof Object[]) return ((Object[])array).length;
if (array instanceof boolean[]) return ((boolean[])array).length;
if (array instanceof byte[]) return ((byte[])array).length;
if (array instanceof char[]) return ((char[])array).length;
if (array instanceof short[]) return ((short[])array).length;
if (array instanceof int[]) return ((int[])array).length;
if (array instanceof long[]) return ((long[])array).length;
if (array instanceof float[]) return ((float[])array).length;
if (array instanceof double[]) return ((double[])array).length;
throw badArray(array);
}
public static boolean getBoolean(Object array, int index) {
if (array instanceof boolean[]) return ((boolean[])array)[index];
throw badArray(array);
}
public static byte getByte(Object array, int index) {
if (array instanceof byte[]) return ((byte[])array)[index];
throw badArray(array);
}
public static char getChar(Object array, int index) {
if (array instanceof char[]) return ((char[])array)[index];
throw badArray(array);
}
public static short getShort(Object array, int index) {
if (array instanceof short[]) return ((short[])array)[index];
if (array instanceof byte[]) return ((byte[])array)[index];
throw badArray(array);
}
public static int getInt(Object array, int index) {
if (array instanceof int[]) return ((int[])array)[index];
if (array instanceof short[]) return ((short[])array)[index];
if (array instanceof char[]) return ((char[])array)[index];
if (array instanceof byte[]) return ((byte[])array)[index];
throw badArray(array);
}
public static long getLong(Object array, int index) {
if (array instanceof long[]) return ((long[])array)[index];
if (array instanceof int[]) return ((int[])array)[index];
if (array instanceof short[]) return ((short[])array)[index];
if (array instanceof char[]) return ((char[])array)[index];
if (array instanceof byte[]) return ((byte[])array)[index];
throw badArray(array);
}
public static float getFloat(Object array, int index) {
if (array instanceof float[]) return ((float[])array)[index];
if (array instanceof long[]) return ((long[])array)[index];
if (array instanceof int[]) return ((int[])array)[index];
if (array instanceof short[]) return ((short[])array)[index];
if (array instanceof char[]) return ((char[])array)[index];
if (array instanceof byte[]) return ((byte[])array)[index];
throw badArray(array);
}
public static double getDouble(Object array, int index) {
if (array instanceof double[]) return ((double[])array)[index];
if (array instanceof float[]) return ((float[])array)[index];
if (array instanceof long[]) return ((long[])array)[index];
if (array instanceof int[]) return ((int[])array)[index];
if (array instanceof short[]) return ((short[])array)[index];
if (array instanceof char[]) return ((char[])array)[index];
if (array instanceof byte[]) return ((byte[])array)[index];
throw badArray(array);
}
public static Object get(Object array, int index) {
if (array instanceof Object[]) return ((Object[])array)[index];
if (array instanceof boolean[]) return ((boolean[])array)[index];
if (array instanceof byte[]) return ((byte[])array)[index];
if (array instanceof char[]) return ((char[])array)[index];
if (array instanceof short[]) return ((short[])array)[index];
if (array instanceof int[]) return ((int[])array)[index];
if (array instanceof long[]) return ((long[])array)[index];
if (array instanceof float[]) return ((float[])array)[index];
if (array instanceof double[]) return ((double[])array)[index];
throw badArray(array);
}
public static void setBoolean(Object array, int index, boolean z) {
if (array instanceof boolean[]) ((boolean[])array)[index] = z; else
throw badArray(array);
}
public static void setByte(Object array, int index, byte b) {
if (array instanceof byte[]) ((byte[])array)[index] = b; else
if (array instanceof short[]) ((short[])array)[index] = b; else
if (array instanceof int[]) ((int[])array)[index] = b; else
if (array instanceof long[]) ((long[])array)[index] = b; else
if (array instanceof float[]) ((float[])array)[index] = b; else
if (array instanceof double[]) ((double[])array)[index] = b; else
throw badArray(array);
}
public static void setChar(Object array, int index, char c) {
if (array instanceof char[]) ((char[])array)[index] = c; else
if (array instanceof int[]) ((int[])array)[index] = c; else
if (array instanceof long[]) ((long[])array)[index] = c; else
if (array instanceof float[]) ((float[])array)[index] = c; else
if (array instanceof double[]) ((double[])array)[index] = c; else
throw badArray(array);
}
public static void setShort(Object array, int index, short s) {
if (array instanceof short[]) ((short[])array)[index] = s; else
if (array instanceof int[]) ((int[])array)[index] = s; else
if (array instanceof long[]) ((long[])array)[index] = s; else
if (array instanceof float[]) ((float[])array)[index] = s; else
if (array instanceof double[]) ((double[])array)[index] = s; else
throw badArray(array);
}
public static void setInt(Object array, int index, int i) {
if (array instanceof int[]) ((int[])array)[index] = i; else
if (array instanceof long[]) ((long[])array)[index] = i; else
if (array instanceof float[]) ((float[])array)[index] = i; else
if (array instanceof double[]) ((double[])array)[index] = i; else
throw badArray(array);
}
public static void setLong(Object array, int index, long l) {
if (array instanceof long[]) ((long[])array)[index] = l; else
if (array instanceof float[]) ((float[])array)[index] = l; else
if (array instanceof double[]) ((double[])array)[index] = l; else
throw badArray(array);
}
public static void setFloat(Object array, int index, float f) {
if (array instanceof float[]) ((float[])array)[index] = f; else
if (array instanceof double[]) ((double[])array)[index] = f; else
throw badArray(array);
}
public static void setDouble(Object array, int index, double d) {
if (array instanceof double[]) ((double[])array)[index] = d; else
throw badArray(array);
}
public static void set(Object array, int index, Object value) {
if (array instanceof Object[]) {
try {
((Object[])array)[index] = value;
} catch (ArrayStoreException e) {
throw badArray(array);
}
} else
if (value instanceof Boolean) setBoolean(array, index, (boolean)value); else
if (value instanceof Byte) setByte(array, index, (byte)value); else
if (value instanceof Short) setShort(array, index, (short)value); else
if (value instanceof Character) setChar(array, index, (char)value); else
if (value instanceof Integer) setInt(array, index, (int)value); else
if (value instanceof Long) setLong(array, index, (long)value); else
if (value instanceof Float) setFloat(array, index, (float)value); else
if (value instanceof Double) setDouble(array, index, (double)value); else
throw badArray(array);
}
}
The get/set/getLength methods of java.lang.reflect.Array are very slow. The existing methods are native methods, and in the C source file sits a lonely "TODO: Performance" comment which seems to have been there for at least a decade, and maybe longer.
I've written a simple re-implementation of the methods in pure Java code, pasted below (released into public domain or whatever license you would need if you wanted to use it). These methods are exactly compatible in the conversions they allow and don't allow and the types of exceptions they throw.
The pure Java methods are faster. They are up to five times faster on the HotSpot Client VM and up to a hundred times faster on the HotSpot Server VM. I do know how to microbenchmark properly on a JIT. The reason for the massive difference is because the existing implementation really is that atrocious.
It's not too surprising that the Java implementation is competitive. The methods are fairly short and straightforward, so modern HotSpot can implement them about as well as C can, plus has all the advantages of aggressive runtime inlining that C can never do, and without the overhead of the transition between Java and native code.
The getLength method is the only potential surprise: whereas the native implementation can presumably read the length from the array object header regardless of its type, the Java implementation needs to cast it to the correct array type since the length field is not part of a common superclass. What's surprising is that the pure Java getLength method is faster despite that. I suppose the cost of the transition between Java and native code is what cripples the performance of the native version.
JUSTIFICATION :
1. Performance. The magnitude of the improvement is great.
2. In general, increasing the amount of the Java platform that is written in Java rather than C increases the maintainability and portability of the platform (and is a minor point of language pride).
CAVEAT :
If value types (and corresponding arrays of value types) are implemented in a future version of the Java language, it's unclear whether reflection of such arrays would be better implemented in Java or in C.
SOURCE :
public class Array {
/*
* Legal conversions:
* From \ To| boolean | byte | char | short | int | long | float | double
* ----------|-------------------------------------------------------------
* boolean | #
* byte | # # # # # #
* char | # # # # #
* short | # # # # #
* int | # # # #
* long | # # #
* float | # #
* double | #
*/
private static RuntimeException badArray(Object array) {
if (array == null)
return new NullPointerException("Array argument is null");
else if (!array.getClass().isArray())
return new IllegalArgumentException("Argument is not an array");
else
return new IllegalArgumentException("Array is of incompatible type");
}
public static int getLength(Object array) {
// Note: all types of multidimensional arrays are instanceof Object[]
if (array instanceof Object[]) return ((Object[])array).length;
if (array instanceof boolean[]) return ((boolean[])array).length;
if (array instanceof byte[]) return ((byte[])array).length;
if (array instanceof char[]) return ((char[])array).length;
if (array instanceof short[]) return ((short[])array).length;
if (array instanceof int[]) return ((int[])array).length;
if (array instanceof long[]) return ((long[])array).length;
if (array instanceof float[]) return ((float[])array).length;
if (array instanceof double[]) return ((double[])array).length;
throw badArray(array);
}
public static boolean getBoolean(Object array, int index) {
if (array instanceof boolean[]) return ((boolean[])array)[index];
throw badArray(array);
}
public static byte getByte(Object array, int index) {
if (array instanceof byte[]) return ((byte[])array)[index];
throw badArray(array);
}
public static char getChar(Object array, int index) {
if (array instanceof char[]) return ((char[])array)[index];
throw badArray(array);
}
public static short getShort(Object array, int index) {
if (array instanceof short[]) return ((short[])array)[index];
if (array instanceof byte[]) return ((byte[])array)[index];
throw badArray(array);
}
public static int getInt(Object array, int index) {
if (array instanceof int[]) return ((int[])array)[index];
if (array instanceof short[]) return ((short[])array)[index];
if (array instanceof char[]) return ((char[])array)[index];
if (array instanceof byte[]) return ((byte[])array)[index];
throw badArray(array);
}
public static long getLong(Object array, int index) {
if (array instanceof long[]) return ((long[])array)[index];
if (array instanceof int[]) return ((int[])array)[index];
if (array instanceof short[]) return ((short[])array)[index];
if (array instanceof char[]) return ((char[])array)[index];
if (array instanceof byte[]) return ((byte[])array)[index];
throw badArray(array);
}
public static float getFloat(Object array, int index) {
if (array instanceof float[]) return ((float[])array)[index];
if (array instanceof long[]) return ((long[])array)[index];
if (array instanceof int[]) return ((int[])array)[index];
if (array instanceof short[]) return ((short[])array)[index];
if (array instanceof char[]) return ((char[])array)[index];
if (array instanceof byte[]) return ((byte[])array)[index];
throw badArray(array);
}
public static double getDouble(Object array, int index) {
if (array instanceof double[]) return ((double[])array)[index];
if (array instanceof float[]) return ((float[])array)[index];
if (array instanceof long[]) return ((long[])array)[index];
if (array instanceof int[]) return ((int[])array)[index];
if (array instanceof short[]) return ((short[])array)[index];
if (array instanceof char[]) return ((char[])array)[index];
if (array instanceof byte[]) return ((byte[])array)[index];
throw badArray(array);
}
public static Object get(Object array, int index) {
if (array instanceof Object[]) return ((Object[])array)[index];
if (array instanceof boolean[]) return ((boolean[])array)[index];
if (array instanceof byte[]) return ((byte[])array)[index];
if (array instanceof char[]) return ((char[])array)[index];
if (array instanceof short[]) return ((short[])array)[index];
if (array instanceof int[]) return ((int[])array)[index];
if (array instanceof long[]) return ((long[])array)[index];
if (array instanceof float[]) return ((float[])array)[index];
if (array instanceof double[]) return ((double[])array)[index];
throw badArray(array);
}
public static void setBoolean(Object array, int index, boolean z) {
if (array instanceof boolean[]) ((boolean[])array)[index] = z; else
throw badArray(array);
}
public static void setByte(Object array, int index, byte b) {
if (array instanceof byte[]) ((byte[])array)[index] = b; else
if (array instanceof short[]) ((short[])array)[index] = b; else
if (array instanceof int[]) ((int[])array)[index] = b; else
if (array instanceof long[]) ((long[])array)[index] = b; else
if (array instanceof float[]) ((float[])array)[index] = b; else
if (array instanceof double[]) ((double[])array)[index] = b; else
throw badArray(array);
}
public static void setChar(Object array, int index, char c) {
if (array instanceof char[]) ((char[])array)[index] = c; else
if (array instanceof int[]) ((int[])array)[index] = c; else
if (array instanceof long[]) ((long[])array)[index] = c; else
if (array instanceof float[]) ((float[])array)[index] = c; else
if (array instanceof double[]) ((double[])array)[index] = c; else
throw badArray(array);
}
public static void setShort(Object array, int index, short s) {
if (array instanceof short[]) ((short[])array)[index] = s; else
if (array instanceof int[]) ((int[])array)[index] = s; else
if (array instanceof long[]) ((long[])array)[index] = s; else
if (array instanceof float[]) ((float[])array)[index] = s; else
if (array instanceof double[]) ((double[])array)[index] = s; else
throw badArray(array);
}
public static void setInt(Object array, int index, int i) {
if (array instanceof int[]) ((int[])array)[index] = i; else
if (array instanceof long[]) ((long[])array)[index] = i; else
if (array instanceof float[]) ((float[])array)[index] = i; else
if (array instanceof double[]) ((double[])array)[index] = i; else
throw badArray(array);
}
public static void setLong(Object array, int index, long l) {
if (array instanceof long[]) ((long[])array)[index] = l; else
if (array instanceof float[]) ((float[])array)[index] = l; else
if (array instanceof double[]) ((double[])array)[index] = l; else
throw badArray(array);
}
public static void setFloat(Object array, int index, float f) {
if (array instanceof float[]) ((float[])array)[index] = f; else
if (array instanceof double[]) ((double[])array)[index] = f; else
throw badArray(array);
}
public static void setDouble(Object array, int index, double d) {
if (array instanceof double[]) ((double[])array)[index] = d; else
throw badArray(array);
}
public static void set(Object array, int index, Object value) {
if (array instanceof Object[]) {
try {
((Object[])array)[index] = value;
} catch (ArrayStoreException e) {
throw badArray(array);
}
} else
if (value instanceof Boolean) setBoolean(array, index, (boolean)value); else
if (value instanceof Byte) setByte(array, index, (byte)value); else
if (value instanceof Short) setShort(array, index, (short)value); else
if (value instanceof Character) setChar(array, index, (char)value); else
if (value instanceof Integer) setInt(array, index, (int)value); else
if (value instanceof Long) setLong(array, index, (long)value); else
if (value instanceof Float) setFloat(array, index, (float)value); else
if (value instanceof Double) setDouble(array, index, (double)value); else
throw badArray(array);
}
}
- relates to
-
JDK-8209554 [lworld] ClassCastException thrown for JCK test instead of expected IllegalArgumentException
- Open