循环反向真的更快吗?

2020/10/08 11:21 · javascript ·  · 0评论

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

我假设这是因为循环不再需要在每次检查以查看是否完成时都求值一个属性,而只需要检查最终的数值。

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

不是i--比快i++实际上,它们的速度都一样快。

在递增循环中需要花费时间的是,每个循环都需要评估i数组的大小。在此循环中:

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

.length当您声明时i评估一次,而对于此循环

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

当您检查是否.length每次递增时i您都会进行评估i <= array.length

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

这个人在许多浏览器中比较了javascript中的很多循环。他还具有测试套件,因此您可以自己运行它们。

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

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

我试图用这个答案来概括一下。

最近测试该问题之前,我一直相信以下括号中的想法

[[就C / C ++之低级语言而言,代码被编译,以便当变量为零(或非零)时,处理器具有特殊的条件跳转命令。
另外,如果你在乎这么多的优化,你可以去
代替,因为是单处理器的命令而手段。]

++ii++++ii++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功能(取决于浏览器),使其使用最新的最佳技巧即可!:) @AMK表示在特殊情况下值得使用array.poparray.shift如果这样做,请将其放在窗帘后面。最大矫枉过正是添加选项,forEach选择循环算法。]

此外,对于低级语言,最佳实践是尽可能使用一些智能库函数来执行复杂的循环操作。

这些库还可以使事物(多线程)不为所动,并且专业的程序员可以使它们保持最新状态。

我进行了更多的检查,结果发现在C / C ++中,即使对于5e9 =(50,000x100,000)的操作,如果对@alestanis这样的常量进行测试,则向上和向下之间也没有区别(JsPerf的结果有时不一致,但是总的说来是一样的:您不可能有什么大的不同。)

因此,
--i恰好是“正题”。它只会使您看起来像一个更好的程序员。:)

另一方面,在这种5e9情况下展开时,它使我从10秒时的12秒降低到2.5秒,而到20秒时又使我从2.1秒降低到2.1秒。这是没有优化的,而优化使事情减少到了无法估量的时间。:)(可以通过上面的方式或使用来完成展开操作i++,但这无法使JavaScript发挥作用。)

总而言之:在面试中保持i--/i++++i//i++不同,坚持使用array.forEach其他复杂的库功能。;)

i-- 快如 i++

下面的代码与您的代码一样快,但是使用了一个额外的变量:

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

建议不要每次都评估数组的大小。对于大型阵列,可以看到性能下降。

由于您对此主题感兴趣,因此请查看Greg Reimer的有关JavaScript循环基准的博客文章,“用JavaScript编写循环的最快方法是什么?

我为JavaScript中不同的循环编码方式构建了一个循环基准测试套件。已经有一些这样的方法了,但是我没有发现任何承认本机数组和HTML集合之间的区别的方法。

您也可以通过打开循环进行性能测试https://blogs.oracle.com/greimer/resource/loop-test.html(如果JavaScript在浏览器中被NoScript阻止,则无法使用)。

编辑:

米兰Adamovsky创建一个更近的基准可以在运行时进行不同的浏览器。

为了在Mac OS X 10.6上的Firefox 17.0中进行测试,我得到了以下循环:

var i, result = 0;
for (i = steps - 1; i; i--) {
  result += i;
}

最快的是:

var result = 0;
for (var i = steps - 1; i >= 0; i--) {
  result += i;
}

基准示例。

不是--or ++,而是比较操作。有了--您可以使用与0的比较,同时用++你需要将它与长度进行比较。在处理器上,通常可以使用零进行比较,而与有限整数进行比较则需要相减。

a++ < length

实际上被编译为

a++
test (a-length)

因此,编译时它在处理器上花费的时间更长。

简短答案

对于普通代码,尤其是像JavaScript这样的高级语言,在i++和中没有性能差异i--

性能标准是for循环和compare语句中的用法

适用于所有高级语言,并且基本上独立于JavaScript的使用。解释是在最后一行生成的汇编代码。

详细说明

循环中可能会出现性能差异。背景是,在汇编代码级别上,您可以看到acompare 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寄存器的变量这可能会在代码中调用额外的寄存器交换,并且语句需要更昂贵的compareif语句。

只有我的5美分

对于高级语言而言,有助于提高可维护性的可读性作为次要的性能改进更为重要。

通常,从数组开始到结束经典迭代效果更好。

从数组末尾到起点的更快迭代会导致不必要的反向序列。

投稿后

至于问评论:的差异--ii--在评价i减量之前或之后。

最好的解释是尝试一下;-)这是一个Bash示例。

 % i=10; echo "$((--i)) --> $i"
 9 --> 9
 % i=10; echo "$((i--)) --> $i"
 10 --> 9

我在Sublime Text 2中看到了相同的建议。

就像已经说过的那样,主要的改进不是在for循环的每次迭代中都评估了数组的长度。这是一种众所周知的优化技术,当数组是HTML文档的一部分时,它在JavaScript中特别有效(for对所有li元素做a )。

例如,

for (var i = 0; i < document.getElementsByTagName('li').length; i++)

比慢得多

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

从我的立场出发,您问题中表格的主要改进是它没有声明额外的变量(len在我的示例中)

但是,如果您问我,重点不在于i++vsi--优化,而是关于不必在每次迭代中评估数组的长度(您可以在jsperf上看到基准测试)。

我认为说这i--比使用i++JavaScript更快是没有道理的

首先,它完全取决于JavaScript引擎的实现。

其次,假设提供了最简单的JIT结构并将其转换为本机指令,则i++vsi--将完全取决于执行它的CPU。也就是说,在ARM(移动电话)上,由于递减并与零比较是在单个指令中执行的,因此下降到0更快。

可能您认为一个比另一个浪费,因为建议的方式是

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

但是建议的方法不是因为一个比另一个快,而是因为如果您编写

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

然后必须对每次迭代array.length进行评估(更智能的JavaScript引擎可能会发现循环不会更改数组的长度)。即使看起来很简单,它实际上也是JavaScript引擎在后台调用的某些函数。

另一个原因(i--可以被认为“更快”)是因为JavaScript引擎只需要分配一个内部变量来控制循环(变量即可var i)。如果将其与array.length或其他变量进行比较,则必须有多个内部变量来控制循环,并且内部变量的数量是JavaScript引擎的有限资产。循环中使用的变量越少,JIT进行优化的机会就越大。这就是为什么i--可以考虑更快...

由于没有其他答案似乎可以回答您的特定问题(超过一半的答案显示了C示例并讨论了底层语言,因此您的问题是针对JavaScript的),因此我决定编写自己的答案。

因此,在这里您可以:

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

测试结果:正如“证明”这个jsPerf,arr.pop()实际上是迄今为止速度最快的循环。但是,专注于--ii--i++++i你在你的问题问,这里有jsPerf(他们是从多个jsPerf的,请参见下面的源)的结果总结如下:

--ii--Firefox中相同,而i--在Chrome中更快。

在Chrome中,基本的for循环(for (var i = 0; i < arr.length; i++))比Firefoxi----i而在Firefox中则更慢。

在Chrome和Firefox中arr.length,Chrome的缓存速度明显提高了170,000个操作/秒。

没有显着差异,++i它比i++大多数浏览器AFAIK都快,在任何浏览器中都没有反之。

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

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

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

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

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

我上次为它烦恼是在编写6502汇编语言时(是8位,是的!)。最大的好处是大多数算术运算(尤其是递减)都更新了一组标志,其中一个标志是Z“到达零”指示器。

因此,在循环结束时,您仅执行了两条指令:(DEC减量)和JNZ(如果不为零则跳转),不需要比较!

最终可以通过JavaScript(和所有语言)将其解释为可在CPU上运行的操作码。CPU始终只有一条指令与零进行比较,这真是太快了。

顺便说一句,如果您可以保证count始终为>= 0,则可以简化为:

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

这不依赖于--++符号,而是取决于您在循环中应用的条件。

例如:如果变量具有静态值,则循环比每次循环检查条件(例如数组的长度或其他条件)要快。

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

for(var i = array.length; i--; )不会很快。但是,当你更换array.length使用super_puper_function(),这可能是显著快(因为它被称为在每次迭代)。就是这样。

如果要在2014年进行更改,则无需考虑优化。如果要使用“搜索并替换”进行更改,则无需考虑优化。如果没有时间,则无需考虑优化。但是现在,您有时间考虑一下。

PS:i--不比快i++

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

首先,您可以自己对其进行测试:

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

因此,据您所知,在任何环境下性能之间都没有区别。

如果要提高脚本的性能,可以尝试执行以下操作:

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

但是您必须了解,可以获得的改进将是微不足道的,以至于大多数情况下您甚至都不在乎它。

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

很久很久以前,就有一条通用的机器指令DSZ(减量和零位跳过)。用汇编语言编程的人使用此指令来实现循环以保存寄存器。现在,这个古老的事实已经过时了,我敢肯定,使用这种伪改进,您不会在任何语言上获得任何性能改进。

我认为,这种知识在我们时代传播的唯一途径是您阅读他人的个人密码。看到这样的构造,并询问为什么要实现,在这里给出答案:“由于与零相比,它提高了性能”。您对您的同事的更高知识感到迷惑,并认为使用它会变得更加聪明:-)

对jsbench进行了比较

正如alestani指出的那样,在递增循环中需要花费时间的一件事是评估每次迭代的数组大小。在此循环中:

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

.length每次增加,您都会进行评估i在这个:

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

.length声明时,您只评估一次i在这个:

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];
}

如果您可以内联代码,而不用调用函数而不牺牲易读性,则可以使循环快一个数量级!


注意:随着浏览器越来越擅长内联简单函数,它实际上取决于代码的复杂程度。因此,在优化之前先进行分析,因为

  1. 瓶颈可能在其他地方(ajax,reflow等)
  2. 您可能会选择更好的算法
  3. 您可以选择更好的数据结构

但要记住:

编写代码供人们阅读,而只是为了让机器执行。

您现在的操作方式并没有更快(除了不确定的循环之外,我想您打算这样做)i--

如果要使其更快,请执行以下操作:

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

当然,在这么小的循环中您不会注意到它。之所以更快,是因为您在检查i为“ true”时递减i(当其为0时其值为“ false”)

有时,对我们编写代码的方式进行一些非常小的更改可能会对我们的代码实际运行速度产生重大影响。一个小的代码更改可以对执行时间产生很大影响的区域是我们有一个处理数组的for循环。如果数组包含网页上的元素(例如单选按钮),则更改的影响最大,但是即使数组位于Javascript代码内部,也仍然值得应用此更改。

编码for循环以处理数组lis的常规方法如下:

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

这样的问题是,使用myArray.length评估数组的长度会花费时间,而我们对循环进行编码的方式意味着必须在每次循环时都执行此评估。如果数组包含1000个元素,则数组的长度将被评估1001次。如果我们正在查看单选按钮,并且具有myForm.myButtons.length,那么评估将花费更长的时间,因为必须首先放置指定表单内的适当按钮组,然后才能在每次循环前评估长度。

显然,我们不希望在处理数组时改变其长度,因此对长度的所有这些重新计算只会不必要地增加处理时间。(当然,如果循环中有添加或删除数组条目的代码,则数组大小可以在迭代之间更改,因此我们无法更改对其进行测试的代码)

对于固定大小的循环,我们可以纠正此问题的方法是在循环开始时计算一次长度并将其保存在变量中。然后,我们可以测试变量以确定何时终止循环。这比每次评估数组长度都快得多,特别是当数组包含的条目不止几个或者是网页的一部分时。

执行此操作的代码是:

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

因此,现在我们只评估一次数组的大小,并针对每次循环时保存该值的变量测试循环计数器。这个额外的变量可以比评估数组的大小更快地被访问,因此我们的代码将比以前更快地运行。我们的脚本中只有一个额外的变量。

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

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

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

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

++vs.--无关紧要,因为JavaScript是一种解释性语言,而不是一种编译语言。每条指令翻译成一种以上的机器语言,因此您不必在乎细节。

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

您应该编写可读的代码。

在许多情况下,这与处​​理器可以比其他比较更快地将零进行比较这一事实基本无关。

这是因为只有少数Javascript引擎(JIT列表中的Java引擎)实际生成机器语言代码。

大多数Javascript引擎都会构建源代码的内部表示形式,然后对其进行解释(要了解其本质,请在Firefox的SpiderMonkey该页面底部附近查看)。通常,如果一段代码实际上做同样的事情,但是导致内部表示更简单,那么它将运行得更快。

请记住,对于简单的任务(例如,将一个变量与变量进行加/减或将变量与某物进行比较),解释器从一个内部“指令”转移到下一个“指令”的开销非常高,因此,较少的“指令” JS引擎内部使用的效果更好。

好吧,我不了解JavaScript,这实际上应该只是重新评估数组长度的问题,并且可能与关联数组有关(如果仅递减,则不太可能需要分配新条目-如果阵列是密集的,也就是说,有人可以对此进行优化)。

在低级汇编中,有一个称为DJNZ的循环指令(如果非零,则递减和跳转)。因此,减量和跳转全都在一条指令中,因此可能比INC和JL / JB稍快一些(增量,如果小于则跳转,如果小于则跳转)。同样,与零比较比与另一个数字比较容易。但是,所有这些实际上都是微不足道的,并且还取决于目标体系结构(可能会有所作为,例如,在智能手机中的Arm上可能会有所作为)。

我不希望这些低级的差异会对解释语言产生如此大的影响,我只是没有在答复中看到DJNZ,所以我想我会分享一个有趣的想法。

使用据说是--I更快(在C ++),因为仅存在一个结果,递减的值。i--需要将递减后的值存储回i,并且还要保留原始值作为结果(j = i--;)。在大多数编译器中,这占用了两个寄存器而不是一个寄存器,这可能导致另一个变量必须写入内存,而不是保留为寄存器变量。

我同意那些说这几天没有影响的人。

简单来说

“ i--和i ++。实际上,它们都花费相同的时间”。

但是在这种情况下,当您进行增量操作时,处理器会在每次变量增加1时评估.length,在递减的情况下。尤其是在这种情况下,它将仅评估.length一次,直到得到0。

首先,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] };

由于编译器的优化,PS Slow仅对几种语言(JavaScript引擎)慢。最好的方法是使用'<'代替'<='(或'=')和'--i'代替'i--'

i--或i ++并没有花费很多时间。如果深入研究CPU体系结构,++则其速度要快于--,因为该--操作将完成2的补数,但是它发生在硬件内部,因此这将使其变得快速,并且,++并且--这些操作之间的主要区别没有在CPU中消耗的时间最少。

for循环运行是这样的:

  • 在开始时初始化一次变量。
  • 检查约束在环路的第二个操作数,<><=,等。
  • 然后应用循环。
  • 增加循环并再次循环将再次引发这些过程。

所以,

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

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

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

将在每个循环中计算长度,因此将花费大量时间。

回答此类问题的最佳方法是实际尝试。设置一个可以计算一百万次迭代的循环,并以两种方式进行。为两个循环计时,并比较结果。

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

喜欢它,很多标记但没有答案:D

简单地与零进行比较始终是最快的比较

所以(a == 0)实际上比(a == 5)返回True更快

它很小且微不足道,并且在一个集合中有1亿行是可以测量的。

即在循环上,您可能会说我<= array.length并在递增i

在下降循环中,您可能会说i> = 0并在递减i。

比较起来更快。不是循环的“方向”。

帮助其他人避免头痛---投票!

此页面上最流行的答案不适用于Firefox 14,并且未通过jsLinter。“ while”循环需要比较运算符,而不是赋值。它确实适用于chrome,safari,甚至也可以。但是死于Firefox。

这已破了!

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

这将起作用!(在Firefox上运行,通过jsLinter)

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

这只是一个猜测,但这也许是因为处理器比较具有0(i> = 0)而不是具有另一个值(i <Things.length)的值比较容易。

本文地址:http://javascript.askforanswer.com/xunhuanfanxiangzhendegengkuaima.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇: