这话我听过好几次了。JavaScript循环在向后计数时真的更快吗?如果有,为什么?我已经看到了一些测试套件示例,它们显示反向循环更快,但我找不到任何解释!
我假设这是因为循环不再需要在每次检查属性时评估它是否已经完成,它只检查最终的数值。
即。
for (var i = count - 1; i >= 0; i--)
{
// count is only evaluated once and then the comparison is always on 0.
}
这话我听过好几次了。JavaScript循环在向后计数时真的更快吗?如果有,为什么?我已经看到了一些测试套件示例,它们显示反向循环更快,但我找不到任何解释!
我假设这是因为循环不再需要在每次检查属性时评估它是否已经完成,它只检查最终的数值。
即。
for (var i = count - 1; i >= 0; i--)
{
// count is only evaluated once and then the comparison is always on 0.
}
当前回答
好吧,我不了解JavaScript,它应该只是一个重新计算数组长度的问题,也许与关联数组有关(如果你只是递减,不太可能需要分配新条目——如果数组是密集的,也就是说。有人可能会为此进行优化)。
在低级汇编中,有一个循环指令,称为DJNZ(如果非零则递减和跳跃)。因此,递减和跳转都在一条指令中,使得它可能比INC和JL / JB略快(递增,如果小于则跳转/如果低于则跳转)。同样,与零比较比与其他数字比较简单。但所有这些都是边缘的,也取决于目标架构(可能会产生不同,例如智能手机上的Arm)。
我没想到这种低级别的差异会对解释语言产生这么大的影响,我只是在回答中没有看到DJNZ,所以我想分享一个有趣的想法。
其他回答
简短的回答
对于普通代码,特别是在像JavaScript这样的高级语言中,i++和i——在性能上没有区别。
性能标准是在for循环和compare语句中的使用。
这适用于所有高级语言,并且基本上独立于JavaScript的使用。解释是最后一行的汇编代码。
详细解释
在循环中可能会出现性能差异。背景是,在汇编代码级别上,您可以看到compare with 0只是一个语句,不需要额外的寄存器。
这种比较是在循环的每一次传递中进行的,可能会导致可测量的性能改进。
for(var i = array.length; i--; )
将被求值为如下的伪代码:
i=array.length
:LOOP_START
decrement i
if [ i = 0 ] goto :LOOP_END
... BODY_CODE
:LOOP_END
注意,0是一个字面值,或者换句话说,是一个常量值。
for(var i = 0 ; i < array.length; i++ )
将被计算为如下的伪代码(假设是正常的解释器优化):
end=array.length
i=0
:LOOP_START
if [ i < end ] goto :LOOP_END
increment i
... BODY_CODE
:LOOP_END
注意,end是一个需要CPU寄存器的变量。这可能会在代码中调用额外的寄存器交换,并且需要在if语句中使用更昂贵的compare语句。
只是我的5美分
对于高级语言来说,可读性(有助于可维护性)作为一种较小的性能改进更为重要。
通常,从数组开始到结束的经典迭代更好。
从数组末端到开始的快速迭代会导致可能不需要的反向序列。
附言
正如在评论中问到的:-i和i-的区别在于i在递减之前或之后的求值。
最好的解释是尝试一下;-)下面是一个Bash示例。
% i=10; echo "$((--i)) --> $i"
9 --> 9
% i=10; echo "$((i--)) --> $i"
10 --> 9
好吧,我不了解JavaScript,它应该只是一个重新计算数组长度的问题,也许与关联数组有关(如果你只是递减,不太可能需要分配新条目——如果数组是密集的,也就是说。有人可能会为此进行优化)。
在低级汇编中,有一个循环指令,称为DJNZ(如果非零则递减和跳跃)。因此,递减和跳转都在一条指令中,使得它可能比INC和JL / JB略快(递增,如果小于则跳转/如果低于则跳转)。同样,与零比较比与其他数字比较简单。但所有这些都是边缘的,也取决于目标架构(可能会产生不同,例如智能手机上的Arm)。
我没想到这种低级别的差异会对解释语言产生这么大的影响,我只是在回答中没有看到DJNZ,所以我想分享一个有趣的想法。
既然你对这个主题感兴趣,可以看看Greg Reimer关于JavaScript循环基准的博客文章,用JavaScript编写循环的最快方法是什么?:
我为JavaScript中不同的循环编码方式构建了一个循环基准测试套件。现在已经有一些这样的工具了,但是我还没有找到任何一个能够区分原生数组和HTML集合的工具。
您还可以通过打开https://blogs.oracle.com/greimer/resource/loop-test.html对循环进行性能测试(如果JavaScript在浏览器中被NoScript阻塞,则无法工作)。
编辑:
Milan Adamovsky最近创建的基准测试可以在运行时针对不同的浏览器执行。
在Mac OS X 10.6上的Firefox 17.0测试中,我得到了以下循环:
你现在做的方式不是更快(除了它是一个无限循环,我猜你的意思是做I——。
如果你想让它更快,请:
for (i = 10; i--;) {
//super fast loop
}
当然,在这么小的循环中你不会注意到它。它更快的原因是因为你在检查i是否为“真”时递减i(当它达到0时,它的计算结果为“假”)
它可以解释为JavaScript(以及所有语言)最终被转换为在CPU上运行的操作码。cpu总是只有一条指令用于与0进行比较,这非常快。
顺便说一句,如果你能保证count总是>= 0,你可以简化为:
for (var i = count; i--;)
{
// whatever
}