Bal*_*der 15 java arrays reflection performance
由于我在项目中大量使用对数组的反射访问,我决定比较array[index]vs 的性能java.lang.reflect.Array.get(array, index).虽然我预计,反射调用会慢得多,但我惊讶地发现它们的速度要慢10-16倍.
因此,我决定编写一个简单的实用程序方法,它Array#get通过强制转换对象而不是使用本机方法(同样Array#get)来接收给定索引处的数组:
public static Object get(Object array, int index){
Class<?> c = array.getClass();
if (int[].class == c) {
return ((int[])array)[index];
} else if (float[].class == c) {
return ((float[])array)[index];
} else if (boolean[].class == c) {
return ((boolean[])array)[index];
} else if (char[].class == c) {
return ((char[])array)[index];
} else if (double[].class == c) {
return ((double[])array)[index];
} else if (long[].class == c) {
return ((long[])array)[index];
} else if (short[].class == c) {
return ((short[])array)[index];
} else if (byte[].class == c) {
return ((byte[])array)[index];
}
return ((Object[])array)[index];
}
Run Code Online (Sandbox Code Playgroud)
我相信这个方法提供了相同的功能Array#get,抛出异常的显着差异(例如,如果用没有数组的方法调用方法,则ClassCastException抛出而不是a ).IllegalArgumentExceptionObject
令我惊讶的是,这种实用方法的表现要好得多Array#get.
三个问题:
Array#get,或者这可能是硬件/平台/ Java版本问题(我在双核Windows 7笔记本电脑上使用Java 8进行了测试)?Array#get?即是否必须使用本机调用实现一些功能?Array#get使用本机方法实现,当相同的功能可以在纯Java中以更高的性能实现?测试类和结果
测试是使用Caliper完成的(从编译代码所需的git中最新的Caliper).但为了您的方便,我还包括一个执行简化测试的主方法(您需要删除Caliper注释以使其编译).
识别TestClass:
import java.lang.reflect.Array;
import com.google.caliper.BeforeExperiment;
import com.google.caliper.Benchmark;
public class ArrayAtBenchmark {
public static final class ArrayUtil {
public static Object get(Object array, int index){
Class<?> c = array.getClass();
if (int[].class == c) {
return ((int[])array)[index];
} else if (float[].class == c) {
return ((float[])array)[index];
} else if (boolean[].class == c) {
return ((boolean[])array)[index];
} else if (char[].class == c) {
return ((char[])array)[index];
} else if (double[].class == c) {
return ((double[])array)[index];
} else if (long[].class == c) {
return ((long[])array)[index];
} else if (short[].class == c) {
return ((short[])array)[index];
} else if (byte[].class == c) {
return ((byte[])array)[index];
}
return ((Object[])array)[index];
}
}
private static final int ELEMENT_SIZE = 100;
private Object[] objectArray;
@BeforeExperiment
public void setup(){
objectArray = new Object[ELEMENT_SIZE];
for (int i = 0; i < objectArray.length; i++) {
objectArray[i] = new Object();
}
}
@Benchmark
public int ObjectArray_at(int reps){
int dummy = 0;
for (int i = 0; i < reps; i++) {
for (int j = 0; j < ELEMENT_SIZE; j++) {
dummy |= objectArray[j].hashCode();
}
}
return dummy;
}
@Benchmark
public int ObjectArray_Array_get(int reps){
int dummy = 0;
for (int i = 0; i < reps; i++) {
for (int j = 0; j < ELEMENT_SIZE; j++) {
dummy |= Array.get(objectArray, j).hashCode();
}
}
return dummy;
}
@Benchmark
public int ObjectArray_ArrayUtil_get(int reps){
int dummy = 0;
for (int i = 0; i < reps; i++) {
for (int j = 0; j < ELEMENT_SIZE; j++) {
dummy |= ArrayUtil.get(objectArray, j).hashCode();
}
}
return dummy;
}
// test method to use without Cailper
public static void main(String[] args) {
ArrayAtBenchmark benchmark = new ArrayAtBenchmark();
benchmark.setup();
int warmup = 100000;
// warm up
benchmark.ObjectArray_at(warmup);
benchmark.ObjectArray_Array_get(warmup);
benchmark.ObjectArray_ArrayUtil_get(warmup);
int reps = 100000;
long start = System.nanoTime();
int temp = benchmark.ObjectArray_at(reps);
long end = System.nanoTime();
long time = (end-start)/reps;
System.out.println("time for ObjectArray_at: " + time + " NS");
start = System.nanoTime();
temp |= benchmark.ObjectArray_Array_get(reps);
end = System.nanoTime();
time = (end-start)/reps;
System.out.println("time for ObjectArray_Array_get: " + time + " NS");
start = System.nanoTime();
temp |= benchmark.ObjectArray_ArrayUtil_get(reps);
end = System.nanoTime();
time = (end-start)/reps;
System.out.println("time for ObjectArray_ArrayUtil_get: " + time + " NS");
if (temp == 0) {
// sanity check to prevent JIT to optimize the test methods away
System.out.println("result:" + result);
}
}
}
Run Code Online (Sandbox Code Playgroud)
可在此处查看Caliper结果.
简化主方法的结果在我的机器上看起来像这样:
time for ObjectArray_at: 620 NS
time for ObjectArray_Array_get: 10525 NS
time for ObjectArray_ArrayUtil_get: 1287 NS
Run Code Online (Sandbox Code Playgroud)
附加信息
Array方法(例如Array#getInt,Array#getLength,Array#set等)也表现比类似地实现实用方法慢得多是的,Array.get在OpenJDK/Oracle JDK中运行缓慢,因为它是由本机方法实现的,并未由JIT优化.
Array.get除了从最早的JDK版本(当JVM不是那么好并且根本没有JIT)时,它没有特殊的原因.此外,还有一个纯Java 兼容实现的java.lang.reflect.Array从GNU Classpath的.
目前(从JDK 8u45开始),只优化了Array.newInstance和Array.getLength(是JVM内在函数).看起来没有人真正关心反射get/set方法的性能.但是@ Marco13 注意到有一个公开的问题JDK-8051447来改善Array.*未来某些方法的性能.