这话我听过好几次了。JavaScript循环在向后计数时真的更快吗?如果有,为什么?我已经看到了一些测试套件示例,它们显示反向循环更快,但我找不到任何解释!

我假设这是因为循环不再需要在每次检查属性时评估它是否已经完成,它只检查最终的数值。

即。

for (var i = count - 1; i >= 0; i--)
{
  // count is only evaluated once and then the comparison is always on 0.
}

你现在做的方式不是更快(除了它是一个无限循环,我猜你的意思是做I——。

如果你想让它更快,请:

for (i = 10; i--;) {
    //super fast loop
}

当然,在这么小的循环中你不会注意到它。它更快的原因是因为你在检查i是否为“真”时递减i(当它达到0时,它的计算结果为“假”)


它可以解释为JavaScript(以及所有语言)最终被转换为在CPU上运行的操作码。cpu总是只有一条指令用于与0进行比较,这非常快。

顺便说一句,如果你能保证count总是>= 0,你可以简化为:

for (var i = count; i--;)
{
  // whatever
}

回答这类问题的最好方法就是亲自去尝试。建立一个计算百万次迭代的循环,并以两种方式进行。对两个循环进行计时,并比较结果。

答案可能取决于您使用的浏览器。有些会有不同的结果。


这个人比较了javascript和很多浏览器中的循环。他还提供了一个测试套件,您可以自己运行这些测试。

在所有情况下(除非我在阅读中遗漏了一个),最快的循环是:

var i = arr.length; //or 10
while(i--)
{
  //...
}

使用前缀增量操作符要快一些。对于后缀,编译器必须保留之前的值作为表达式的结果。

for (var i = 0; i < n; ++i) {
  do_stuff();
}

聪明的解释器或编译器会看到i++的结果没有被使用,也不会存储表达式的结果,但不是所有的js引擎都这样做。


爱死它了,很多分数但没有回答:D

简单地把比较放在零总是最快的比较

因此(a==0)返回True比(a==5)更快

它很小且无关紧要,并且集合中有1亿行,因此它是可测量的。

例如,在循环中,你可能会说where I <= array。长度和I的增量

在下行循环中,你可能会说I >= 0,而不是减去I。

比较速度更快。而不是循环的“方向”。


在许多情况下,这本质上与处理器比其他比较更快地比较0这一事实无关。

这是因为只有少数Javascript引擎(JIT列表中的那些)真正生成机器语言代码。

大多数Javascript引擎构建源代码的内部表示,然后对其进行解释(要了解这是什么样子,请查看Firefox的SpiderMonkey页面底部附近)。一般来说,如果一段代码实际上做同样的事情,但导致更简单的内部表示,它将运行得更快。

请记住,对于简单的任务,如从一个变量中添加/减去一个,或将一个变量与某个东西进行比较,解释器从一个内部“指令”移动到下一个“指令”的开销是相当高的,因此JS引擎内部使用的“指令”越少越好。


我想为这个线程贡献最快的JavaScript循环,是跨浏览器的!与反向while循环相比,该循环的性能提高了500%以上。

我的博客:JavaScript中最快的循环


帮助别人避免头痛——投票吧!!

本页上最流行的答案在Firefox 14上不工作,并且没有通过jsLinter。while循环需要比较操作符,而不是赋值操作符。它可以在chrome、safari甚至ie浏览器上运行。但在firefox中消亡了。

这个坏了!

var i = arr.length; //or 10
while(i--)
{
  //...
}

这会有用的!(在firefox上工作,通过jsLinter)

var i = arr.length; //or 10
while(i>-1)
{
  //...
  i = i - 1;
}

并不是说i比i++快。实际上,它们都一样快。

在升序循环中需要花费时间的是,对于每个i,计算数组的大小。在这个循环中:

for(var i = array.length; i--;)

当你声明i时,你只对.length求值一次,而对于这个循环

for(var i = 1; i <= array.length; i++)

当你检查i是否<= array.length时,你每次增加i就计算.length。

在大多数情况下,您甚至不应该担心这种优化。


这只是一个猜测,但这可能是因为处理器更容易将某个值与0 (i >= 0)进行比较,而不是与另一个值(i < Things.length)进行比较。


它取决于数组在内存中的位置,以及访问该数组时内存页面的命中率。

在某些情况下,按列顺序访问数组成员比按行顺序访问快,因为命中率增加了。


I和i++一样快

下面的代码和你的一样快,但使用了一个额外的变量:

var up = Things.length;
for (var i = 0; i < up; i++) {
    Things[i]
};

建议不要每次都计算数组的大小。对于大数组,可以看到性能下降。


不是——或++,而是比较操作。使用——,你可以使用与0比较,而使用++,你需要与长度比较。在处理器上,与零的比较通常是可用的,而与有限整数的比较则需要减法。

a++ < length

实际编译为

a++
test (a-length)

所以它在编译时在处理器上花费的时间更长。


我试图用这个答案给出一个广泛的图景。

以下括号中的想法是我的想法,直到我最近测试了这个问题:

对于像C/ c++这样的低级语言,代码被编译后,当一个变量为零(或非零)时,处理器有一个特殊的条件跳转命令。 同样,如果你关心这么多优化,你可以使用++i而不是i++,因为++i是一个单处理器命令,而i++意味着j=i+1, i=j。

真正快速的循环可以通过展开它们来完成:

for(i=800000;i>0;--i)
    do_it(i);

它可以比

for(i=800000;i>0;i-=8)
{
    do_it(i); do_it(i-1); do_it(i-2); ... do_it(i-7);
}

但原因可能相当复杂(游戏邦注:游戏中存在处理器命令预处理和缓存处理的问题)。

就高级语言而言,比如JavaScript,如果你依赖于循环的库和内置函数,你就可以优化。让他们决定怎么做最好。

因此,在JavaScript中,我建议使用类似

array.forEach(function(i) {
    do_it(i);
});

它也不太容易出错,浏览器也有机会优化您的代码。

[注:不仅是浏览器,而且你也有一个空间来轻松优化,只需重新定义forEach函数(依赖于浏览器),以便它使用最新的最佳技巧!]: @A.M.K。表示在特殊情况下更值得使用数组。Pop或array.shift。如果你这么做了,把它放在窗帘后面。最大的过量是为forEach添加选项来选择循环算法。

此外,对于低级语言,如果可能的话,最好使用一些智能库函数来处理复杂的循环操作。

这些库也可以把东西(多线程)放在你背后,也可以让专门的程序员保持它们的更新。

我仔细研究了一下,发现在C/ c++中, 即使对于5e9 = (50,000x100,000)操作,如果测试是针对@alestanis所说的常量进行的,那么上升和下降之间也没有区别。(JsPerf的结果有时不一致,但总的来说都是一样的:你不能有很大的不同。) 所以——i恰好是一个相当“时髦”的东西。这只会让你看起来像一个更好的程序员。:)

另一方面,在5e9的情况下,当我走10秒时,展开时间从12秒缩短到2.5秒,当我走20秒时,展开时间缩短到2.1秒。它没有优化,而优化使事情缩短到不可估量的时间。:)(展开可以用我上面的方法完成,也可以用i++完成,但这并没有在JavaScript中带来进步。)

总而言之:保持i——/i++和++i/i++的区别来面试,坚持数组。forEach或其他复杂的库函数。;)


这并不依赖于——或++符号,而是取决于你在循环中应用的条件。

例如:如果变量有一个静态值,那么循环会比每次循环都检查条件(比如数组的长度或其他条件)更快。

但是不用担心这种优化,因为这次它的效果是以纳秒为单位衡量的。


我在《Sublime Text 2》中看到了同样的建议。

如前所述,主要的改进不是在for循环的每次迭代中计算数组的长度。这是一种众所周知的优化技术,当数组是HTML文档的一部分时,在JavaScript中特别有效(为所有li元素执行for)。

例如,

For (var I = 0;i < document.getElementsByTagName('li').length;我+ +)

慢得多

for (was i = 0, len = document.getElementsByTagName('li').length; in < fief; i++)

在我看来,你的问题中表单的主要改进是它没有声明一个额外的变量(在我的例子中是len)

但如果你问我,我认为重点不在于i++ vs i——优化,而在于不必在每次迭代时计算数组的长度(您可以看到jsperf上的基准测试)。


好吧,我不了解JavaScript,它应该只是一个重新计算数组长度的问题,也许与关联数组有关(如果你只是递减,不太可能需要分配新条目——如果数组是密集的,也就是说。有人可能会为此进行优化)。

在低级汇编中,有一个循环指令,称为DJNZ(如果非零则递减和跳跃)。因此,递减和跳转都在一条指令中,使得它可能比INC和JL / JB略快(递增,如果小于则跳转/如果低于则跳转)。同样,与零比较比与其他数字比较简单。但所有这些都是边缘的,也取决于目标架构(可能会产生不同,例如智能手机上的Arm)。

我没想到这种低级别的差异会对解释语言产生这么大的影响,我只是在回答中没有看到DJNZ,所以我想分享一个有趣的想法。


For (var I = array.length;我,)快不了多少。但是当你替换数组时。使用super_puper_function(),这可能会快得多(因为它在每次迭代中都被调用)。这就是区别。

如果你打算在2014年改变它,你不需要考虑优化。如果你打算用“搜索和替换”来改变它,你不需要考虑优化。如果你没有时间,你不需要考虑优化。但是现在,你有时间考虑一下。

注:i——并不比i++快。


我最后一次为它烦恼是在编写6502程序集时(8位,是的!)最大的收获是大多数算术操作(特别是减数)更新了一组标志,其中一个是Z,“达到零”指示符。

因此,在循环的最后,您只执行了两条指令:DEC(减量)和JNZ(如果不是零则跳),不需要进行比较!


既然你对这个主题感兴趣,可以看看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(在c++中)更快,因为只有一个结果,即递减的值。I——需要将减后的值存储回I,并保留原始值作为结果(j = I——;)。在大多数编译器中,这会占用两个而不是一个寄存器,这可能会导致另一个变量必须写入内存,而不是作为寄存器变量保留。

我同意其他人的看法,他们说现在没有什么不同。


我不认为在JavaScript中说I比i++更快是有意义的。

首先,它完全依赖于JavaScript引擎实现。

其次,如果最简单的构造被JIT化并转换为本机指令,那么i++ vs i——将完全依赖于执行它的CPU。也就是说,在arm(移动电话)上,下降到0更快,因为递减和比较0是在一条指令中执行的。

也许,你认为一个比另一个更浪费,因为建议的方式是

for(var i = array.length; i--; )

但建议的方式不是因为一个比另一个快,而只是因为如果你写

for(var i = 0; i < array.length; i++)

然后是每个迭代数组。length必须被计算(更聪明的JavaScript引擎可能会发现循环不会改变数组的长度)。尽管它看起来像一个简单的语句,但它实际上是JavaScript引擎在底层调用的某个函数。

The other reason, why i-- could be considered "faster" is because JavaScript engine needs to allocate only one internal variable to control the loop (variable to the var i). If you compared to array.length or to some other variable then there had to be more than one internal variable to control the loop, and the number of internal variables are limited asset of a JavaScript engine. The less variables are used in a loop the more chance JIT has for optimization. That's why i-- could be considered faster...


简而言之:在JavaScript中执行此操作绝对没有区别。

首先,你可以自己测试一下:

Jsperf -对于各种性能来说都是一个很好的平台 用JavaScript进行测试。 http://jsperf.com/inc-vs-dec-2

您不仅可以在任何JavaScript库中测试和运行任何脚本,还可以访问之前编写的所有脚本,以及查看在不同平台上的不同浏览器中执行时间的差异。

所以就你所看到的,在任何环境下,两者的性能没有区别。

如果你想提高脚本的性能,你可以尝试做以下事情:

a = array.length;语句,这样就不会在每次循环中都计算它的值 循环展开http://en.wikipedia.org/wiki/Loop_unwinding

但你必须明白,你所能获得的改善是如此微不足道,大多数情况下你甚至不应该关心它。

我个人认为为什么会出现这样的误解(十二月vs Inc)

很久很久以前,有一个常见的机器指令,DSZ(减量和跳过零)。用汇编语言编程的人使用这条指令来实现循环,以保存寄存器。现在这个古老的事实已经过时了,而且我非常确定使用这个伪改进在任何语言中都不会得到任何性能改进。

我认为这种知识在我们这个时代传播的唯一方式是当你阅读另一个人的代码。看到这样的结构,问为什么要实现它,这里的答案是:“它提高了性能,因为它与零相比”。你对同事的更高知识感到困惑,并认为用它来变得更聪明:-)


编译器不会缓存。length,因此它没有区别,如果你比较0或。length?我想这对于你正在处理的编译器或解释器是非常特定的。

我想说的是,如果你使用的是一个优化的编译器或解释器,那么你不应该担心这个问题,这是语言开发人员应该担心的问题。


有时,对代码的编写方式做一些非常小的更改,可能会对代码的实际运行速度产生很大的影响。一个微小的代码更改可能会对执行时间产生很大影响的地方是我们有一个处理数组的for循环。当数组是网页上的元素(如单选按钮)时,这种改变的影响最大,但即使数组是Javascript代码的内部,也值得应用这种改变。

编写for循环来处理数组的常规方法如下:

for (var i = 0; i < myArray.length; i++) {...

The problem with this is that evaluating the length of the array using myArray.length takes time and the way that we have coded the loop means that this evaluation has to be performed every time around the loop. If the array contains 1000 elements then the length of the array will be evaluated 1001 times. If we were looking at radio buttons and had myForm.myButtons.length then it will take even longer to evaluate since the appropriate group of buttons within the specified form must first be located before the length can be evaluated each time around the loop.

显然,我们不希望数组的长度在处理时发生变化,因此所有这些重新计算长度的操作只是不必要地增加了处理时间。(当然,如果你在循环中有添加或删除数组条目的代码,那么数组大小可以在迭代之间改变,所以我们不能改变测试它的代码)

对于一个大小固定的循环,我们能做的就是在循环开始时计算长度,并将其保存在一个变量中。然后,我们可以测试变量以决定何时终止循环。这比每次计算数组长度要快得多,特别是当数组包含多个条目或是网页的一部分时。

这样做的代码是:

for (var i = 0, var j = myArray.length; i < j; i++) {...

因此,现在我们只计算数组的大小一次,并在每次循环中测试循环计数器是否包含该值的变量。访问这个额外的变量比计算数组的大小要快得多,因此我们的代码将比以前运行得快得多。我们的脚本中只有一个额外的变量。

通常情况下,处理数组的顺序并不重要,只要数组中的所有条目都得到了处理。在这种情况下,我们可以通过去掉刚刚添加的额外变量并以相反的顺序处理数组来使代码稍微快一些。

以最有效的方式处理数组的最终代码是:

for (var i = myArray.length-1; i > -1; i--) {...

这段代码仍然只在开始时计算一次数组的大小,但不是将循环计数器与变量进行比较,而是将其与常量进行比较。由于访问常量比访问变量更有效,并且由于我们的赋值语句比以前少了一个,所以第三个版本的代码现在比第二个版本的效率略高,比第一个版本的效率要高得多。


i——或i++占用的时间并不多。如果你深入到CPU架构中,++比——更快,因为——操作会做2的补位,但它发生在硬件内部,所以这将使它更快,而且++和——之间没有太大的区别,而且这些操作被认为是CPU消耗的时间最少的。

for循环是这样运行的:

在开始时初始化变量一次。 检查循环的第二个操作数中的约束,<,>,<=等。 然后应用循环。 递增循环,循环再次抛出这些进程。

所以,

for (var i = Things.length - 1; i >= 0; i--) {
    Things[i]
}; 

将计算数组的长度只有一次在开始时,这不是很多时间,但是

for(var i = array.length; i--; ) 

将计算每个循环的长度,因此它将消耗大量时间。


简短的回答

对于普通代码,特别是在像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

首先,i++和i——在任何编程语言(包括JavaScript)上花费的时间完全相同。

下面的代码花费的时间大不相同。

快速:

for (var i = 0, len = Things.length - 1; i <= len; i++) { Things[i] };

慢:

for (var i = 0; i <= Things.length - 1; i++) { Things[i] };

因此,下面的代码也需要不同的时间。

快速:

for (var i = Things.length - 1; i >= 0; i--) { Things[i] };

慢:

for (var i = 0; i <= Things.length - 1; i++) { Things[i] };

附:由于编译器的优化,Slow只适用于少数语言(JavaScript引擎)。最好的方法是用'<'代替'<='(或'='),用'——i'代替'i——'。


简单地说

“i——和i++。实际上,它们都需要相同的时间。”

但在这种情况下,当你进行增量操作。处理器计算.length每个时间变量都加1,如果减量..特别是在这种情况下,它只计算一次。length直到得到0。


++还是——并不重要,因为JavaScript是一种解释型语言,而不是编译型语言。每条指令都翻译成一种以上的机器语言,你不应该关心那些血腥的细节。

那些谈论使用——(或++)来有效使用汇编指令的人是错误的。这些指令适用于整数算术,在JavaScript中没有整数,只有数字。

您应该编写可读的代码。


由于其他答案似乎都没有回答你的具体问题(其中一半以上展示了C示例并讨论了较低级的语言,你的问题是针对JavaScript的),我决定自己编写一个。

所以,你看:

简单的答案:i——通常更快,因为它不需要每次运行都与0进行比较,各种方法的测试结果如下所示:

测试结果:正如这个jsPerf“证明”的那样,arr.pop()实际上是迄今为止最快的循环。但是,关注——i, i——,i++和++i,正如你在你的问题中所问的,这里是jsPerf(它们来自多个jsPerf,请参阅下面的来源)的结果总结:

——i和i——在Firefox中是相同的,而i——在Chrome中更快。

在Chrome中,一个基本的for循环(for (var i = 0;I < arrr .length;i++))比i-和-i快,而在Firefox中它较慢。

在Chrome和Firefox中都有缓存的arr。长度明显更快,Chrome领先约17万ops/sec。

在没有显著差异的情况下,++i在大多数浏览器中都比i++快,AFAIK,在任何浏览器中都不会相反。

简短的总结:arr.pop()是目前为止最快的循环;对于特别提到的循环,i——是最快的循环。

资料来源:http://jsperf.com/fastest-array-loops-in-javascript/15, http://jsperf.com/ipp-vs-ppi-2

我希望这能回答你的问题。


递减模式的最快方式:

var iteration = 10000000;
    while(iteration > 0){
        iteration--;
    }

速度比:

var iteration = 10000000;
    while(iteration--);

Javascript微优化测试


我在jsbench上做了一个比较。

正如alestani指出的那样,在升序循环中需要花费时间的一件事是,对于每次迭代,计算数组的大小。在这个循环中:

for ( var i = 1; i <= array.length; i++ )

每次增加i,你就计算。length。

for ( var i = 1, l = array.length; i <= l; i++ )

当你声明i时,你只对.length求值一次。

for ( var i = array.length; i--; )

比较是隐式的,它发生在i递减之前,并且代码非常可读。然而,你在循环中放入的东西会产生很大的不同。

循环调用函数(在其他地方定义):

for (i = values.length; i-- ;) {
  add( values[i] );
}

内联代码循环:

var sum = 0;
for ( i = values.length; i-- ;) {
  sum += values[i];
}

如果你可以内联你的代码,而不是调用一个函数,而不牺牲易读性,你可以有一个数量级的速度循环!


注意:由于浏览器越来越擅长内联简单的函数,这实际上取决于你的代码有多复杂。所以,先配置再优化,因为

瓶颈可能在其他地方(ajax, reflow,…) 你可以选择一个更好的算法 您可以选择更好的数据结构

但是请记住:

代码是为了让人阅读而写的,只是顺便让机器执行。