5

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

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 方法的时间约为。_2 的 4/3 时间,尽管两者都创建了一个新数组并将每个字段都设置为给定值。大多数时候时间可以忽略不计,但这仍然困扰着我 - 为什么 _1 明显变慢?任何人都可以以合理的、JVM 支持的方式检查/确认/向我解释吗?

4

1 回答 1

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出现这么多次 - 以避免在之后丢失对数组的引用fastore),而在第二种情况下,数组引用保持在普通堆栈(保存方法参数和局部变量的地方)。在这种情况下,必须始终读取引用 ( aload_1),因为fastore要求 arrayref 在操作数堆栈上。

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

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

于 2012-06-03T12:27:41.247 回答