[英]Why are local variable length for-loops faster? Doesn't branch prediction reduce the effect of lookup times?
前阵子,当我来的时候,我正在阅读一些Android性能提示 :
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;
}
}
Google说:
zero()
最慢,因为JIT尚无法优化循环中每次迭代一次获取数组长度的成本。
one()
更快。 它将所有内容提取到局部变量中,从而避免了查找。 只有阵列长度才能提供性能优势。
完全有意义。 但是对我的计算机体系结构考试考虑得太多之后,我想起了Branch Predictors :
分支预测器是一种数字电路,它试图猜测在确定之前知道分支(例如,if-then-else结构)将走哪条路。 分支预测器的目的是改善指令管道中的流程。
计算机是否不是假设 i < mArray.length
为 true
,从而并行计算循环条件和循环主体 (并且仅预测最后一个循环的错误分支) ,从而有效地消除了性能损失?
我也在考虑投机执行 :
推测执行是一种优化技术,其中计算机系统执行某些实际上可能不需要的任务……目标是提供更多的并发性……
在这种情况下, 计算机将同时执行代码,就好像循环已经完成,并且好像仍在并发进行一样 ,再次有效地消除了与该条件相关的任何计算成本 (因为计算机已经为将来执行了计算)它计算条件)?
从本质上讲,我试图得出的事实是,即使zero()
的条件要比one()
花费更长的时间进行计算,计算机通常也会在等待检索时计算出正确的代码分支无论如何,都是对条件语句的答案,因此对myAray.length
的查找中的性能损失不重要(无论如何,这就是我的想法)。
这里有我没有意识到的东西吗?
很抱歉问题的长度。
提前致谢。
您链接到的站点注释:
zero()最慢,因为JIT尚无法优化循环中每次迭代一次获取数组长度的成本。
我尚未在Android上进行过测试,但现在我认为这是对的。 这意味着,对于循环的每次迭代,CPU必须执行从内存加载mArray.length
值的代码。 原因是数组的长度可能会更改,因此编译器无法将其视为静态值。
而在one()
选项中,程序员根据对数组长度不变的认识来显式设置len
变量。 由于这是一个局部变量,因此编译器可以将其存储在寄存器中,而不必在每次循环迭代中从内存中加载它。 因此,这将减少循环中执行的指令数量,并使分支更容易预测。
没错,分支预测有助于减少与循环条件检查相关的开销。 但是仍然有可能进行多少推测,因此在每个循环迭代中执行更多的指令会产生额外的开销。 同样,许多移动处理器的分支预测器也不那么先进,并且不支持那么多的推测。
我的猜测是,在使用像HotSpot这样的高级Java JIT的现代台式机处理器上,您不会看到3倍的性能差异。 但是我不确定,尝试尝试可能是一个有趣的实验。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.