设置/返回数组的首选方法

时间:2012-06-03 12:09:25

标签: java arrays performance jvm performance-testing

请比较两种设置/返回数组的方法:

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支持的方式检查/确认/解释它吗?

1 个答案:

答案 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。

我们不应该基于这个字节码做出假设 - 毕竟它被转换为CPU指令,并且最有可能在两种情况下,数组引用都存储在一个CPU寄存器中。否则性能差异会很大。

如果您可以衡量差异并且正在进行低级别优化 - 请选择更快的版本。但我怀疑差异是“可移植的”(取决于架构和JVM版本/实现,您将观察到不同的时序行为)。话虽如此 - 我会选择更具可读性的版本,而不是计算机上更快的版本。