请比较两种设置/返回数组的方法:
static public float[] test_arr_speeds_1( int a ) {
return new float[]{ a, a + 1, a + 2, a + 3, a + 4, a + 5,
a + 6, a + 7, a + 8, a + 9 };
} // or e.g. field = new float... in method
static public float[] test_arr_speeds_2( int a ) {
float[] ret = new float[10];
ret[0] = a;
ret[1] = a + 1;
ret[2] = a + 2;
ret[3] = a + 3;
ret[4] = a + 4;
ret[5] = a + 5;
ret[6] = a + 6;
ret[7] = a + 7;
ret[8] = a + 8;
ret[9] = a + 9;
return ret;
} // or e.g. field[0] = ... in method
两者都生成不同的字节码,两者都可以反编译为以前的状态。在通过分析器(100M迭代,无偏,不同的环境)检查执行时间后,_1方法的时间约为。 4/3 _2的时间,即使两者都创建一个新数组并且都将每个字段设置为给定值。在大多数情况下,时间可以忽略不计,但这仍然让我感到困惑 - 为什么_1明显变慢?任何人都可以以合理的,JVM支持的方式检查/确认/解释它吗?
答案 0 :(得分:6)
这是字节码之间的区别(仅适用于前两项)。第一种方法:
bipush 10
newarray float //creating an array with reference on operand stack
dup
iconst_0
iload_0
i2f
fastore //setting first element
dup
iconst_1
iload_0
iconst_1
iadd
i2f
fastore //setting second element
//...
areturn //returning the top of the operand stack
第二种方法:
bipush 10
newarray float
astore_1 //creating an array and storing it in local variable
aload_1
iconst_0
iload_0
i2f
fastore //setting first element
aload_1
iconst_1
iload_0
iconst_1
iadd
i2f
fastore //setting second element
//...
aload_1
areturn
正如您所看到的,唯一的区别是数组引用在第一个场景中保留在操作数堆栈上(这就是dup
多次出现的原因 - 以避免在{{1}之后丢失对数组的引用在第二种情况下,数组引用保存在 normal 堆栈中(其中保留了方法参数和局部变量)。在这种情况下,必须始终读取引用(fastore
),因为aload_1
要求在操作数堆栈上打开arrayref。
我们不应该基于这个字节码做出假设 - 毕竟它被jit转换为CPU指令,并且最有可能在两种情况下,数组引用都存储在一个CPU寄存器中。否则性能差异会很大。
如果您可以衡量差异并且正在进行低级别优化 - 请选择更快的版本。但我怀疑差异是“可移植的”(取决于架构和JVM版本/实现,您将观察到不同的时序行为)。话虽如此 - 我会选择更具可读性的版本,而不是计算机上更快的版本。