原始数组的现代 for 循环

2022-09-04 21:03:30

基元数组上的 for 循环之间是否存在任何性能差异?

假设:

double[] doubleArray = new double[300000];


for (double var: doubleArray) 
   someComplexCalculation(var);

或:

for ( int i = 0, y = doubleArray.length; i < y; i++)
   someComplexCalculation(doubleArray[i]);

测试结果

我实际上分析了它:

Total timeused for modern loop= 13269ms
Total timeused for old loop   = 15370ms

因此,现代循环实际上运行得更快,至少在我的Mac OSX JVM 1.5上是这样。


答案 1

您手写的“旧”表单执行的指令更少,并且可能更快,尽管您必须在给定的JIT编译器下对其进行分析才能确定。“新”形式绝对不会更快。

如果您查看反汇编的代码(由 Sun 的 JDK 1.5 编译),您将看到“new”表单等效于以下代码:

1: double[] tmp = doubleArray;
2: for (int i = 0, y = tmp.length; i < y; i++) {
3:   double var = tmp[i];
4:   someComplexCalculation(var);
5: }

因此,您可以看到使用了更多的局部变量。在第 1 行处的 to 赋值是“额外”的,但它不会在循环中发生,并且可能无法测量。在第 3 行的分配也是额外的。如果性能存在差异,这将是负责任的。doubleArraytmpvar

第 1 行可能看起来没有必要,但如果数组是在进入循环之前由方法计算的,则缓存结果是样板。

也就是说,我会使用新表单,除非您需要对索引变量执行某些操作。任何性能差异都可能在运行时由 JIT 编译器优化掉,并且新窗体更加清晰。如果您继续“手动”执行此操作,则可能会错过未来的优化。一般来说,一个好的编译器可以很好地优化“愚蠢”的代码,但会偶然发现“智能”代码。


答案 2

我的观点是,你不知道,也不应该猜测。如今,试图超越编译器是徒劳的。

有时人们学习的“模式”似乎可以优化一些操作,但在下一个版本的Java中,这些模式实际上更慢。

始终尽可能清楚地编写它,不要担心优化,直到您手中实际有一些用户规范并且未能满足某些要求,即使这样,在测试之前和之后都要非常小心地运行,以确保您的“修复”实际上改进了它足以使该要求通过。

编译器可以做一些了不起的事情,这些事情会让你的袜子大吃一惊,即使你做了一些迭代某个大范围的测试,如果你的范围较小或改变循环内部发生的事情,它可能会完全不同。

及时编译意味着它偶尔会优于C,并且在某些情况下它没有理由不能超越静态汇编语言(汇编不能事先确定不需要调用,Java有时可以做到这一点。

总而言之:你可以投入到代码中的最大价值是把它写成可读的。