java - 为什么局部可变长度 for 循环更快?分支预测不会减少查找时间的影响吗?

标签 java android performance cpu-architecture

不久前,我正在阅读一些 Android performance tips当我经过时:

Foo[] mArray = ...

public void zero() {
    int sum = 0;
    for (int i = 0; i < mArray.length; ++i) {
        sum += mArray[i].mSplat;
    }
}

public void one() {
    int sum = 0;
    Foo[] localArray = mArray;
    int len = localArray.length;

    for (int i = 0; i < len; ++i) {
        sum += localArray[i].mSplat;
    }
}

谷歌说:

zero() is slowest, because the JIT can't yet optimize away the cost of getting the array length once for every iteration through the loop.

one() is faster. It pulls everything out into local variables, avoiding the lookups. Only the array length offers a performance benefit.

这完全有道理。但是在对我的计算机体系结构考试想得太多之后,我想起了 Branch Predictors :

a branch predictor is a digital circuit that tries to guess which way a branch (e.g. an if-then-else structure) will go before this is known for sure. The purpose of the branch predictor is to improve the flow in the instruction pipeline.

计算机不是假设 i < mArray.length true 因此,并行计算循环条件和循环体(并且只预测最后一个循环的错误分支),有效消除任何性能损失?

我也在考虑Speculative Execution :

Speculative execution is an optimization technique where a computer system performs some task that may not be actually needed... The objective is to provide more concurrency...

在这种情况下,计算机将执行代码,就好像循环已经完成,又好像它仍在并发进行,再一次有效地抵消了与循环相关的任何计算成本条件(因为计算机在计算条件时已经在为 future 执行计算)?

本质上,我想要了解的事实是,即使 zero() 中的条件计算时间比 one() 稍长,计算机通常会在等待检索条件语句的答案时计算正确的代码分支,因此查找 myAray.length 的性能损失应该没关系(反正我是这么想的)。

这里有什么我没有意识到的吗?


抱歉问题的长度。

提前致谢。

最佳答案

您链接到笔记的网站:

zero() is slowest, because the JIT can't yet optimize away the cost of getting the array length once for every iteration through the loop.

我还没有在 Android 上测试过,但我假设现在是这样。这意味着对于循环的每次迭代,CPU 都必须执行从内存中加载 mArray.length 值的代码。原因是数组的长度可能会发生变化,因此编译器无法将其视为静态值。

而在 one() 选项中,程序员根据数组长度不会改变的知识显式设置 len 变量。由于这是一个局部变量,编译器可以将它存储在一个寄存器中,而不是在每次循环迭代中从内存中加载它。因此,这将减少循环中执行的指令数,并使分支更容易预测。

您说得对,分支预测有助于减少与循环条件检查相关的开销。但推测的可能性仍然有限,因此在每次循环迭代中执行更多指令会产生额外的开销。此外,许多移动处理器的分支预测器不太先进,不支持那么多的推测。

我的猜测是,在使用像 HotSpot 这样的高级 Java JIT 的现代桌面处理器上,您不会看到 3 倍的性能差异。但我不确定,这可能是一个有趣的实验。

关于java - 为什么局部可变长度 for 循环更快?分支预测不会减少查找时间的影响吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37708339/

相关文章:

android - 如何在异步任务完成之前停止执行 UI 线程以返回在 android 中的 AsyncTask 中获得的值

c - switch 语句中 case 的顺序会影响性能吗?

sql-server - SET-ting ALLOW_SNAPSHOT_ISOLATION ON 的含义是什么?

java - 为什么 Spring Data 的 MongoRepository 如此有限?

java - 检查颜色是否已在 Java 数组中

java - 无法在 Android 中将 java 字符串转换为 JSON 数组

c# - 为什么 Enumerable.Range 比直接 yield 循环更快?

java - 实现 Restful Web 服务最佳实践?

java - 等待ParallelFlux完成

android - 如何以编程方式创建/禁用 intent-filter?