反向循环真的更快吗?

IT技术 javascript performance loops for-loop while-loop
2021-01-31 13:21:13

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

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

IE

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

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

在升序循环中需要时间的是评估每个i数组的大小。在这个循环中:

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

.length评估一次,当你声明时i,而对于这个循环

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

您评估.length每次递增时i,当你检查i <= array.length

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

值得一提的是,我们说的是Ruby、Python等解释型语言。编译语言(例如 Java)在编译器级别进行了优化,这将“平滑”这些差异,以至于无论是否.length在声明中都无关紧要for loop
2021-03-12 13:21:13
@Dredel 博士:这不是比较——而是评估。0比 评估更快array.length嗯,据说。
2021-04-01 13:21:13
是否值得为 array.length 引入一个变量并在 for 循环的头部使用它?
2021-04-02 13:21:13
@ragatskynet:不,除非您正在设置基准并想说明一点。
2021-04-04 13:21:13
@ragatskynet 这取决于:评估.length一定次数或声明和定义新变量会更快吗?在大多数情况下,这是过早的(和错误的)优化,除非您的length评估成本非常高。
2021-04-05 13:21:13

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

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

var i = arr.length; //or 10
while(i--)
{
  //...
}
有趣,但我想知道 pre-decrement 是否会更快。因为它不必存储 i 的中间值。
2021-03-12 13:21:13
@tvanfosson 如果您预先递减,--i那么您必须var current = arr[i-1];在循环内使用,否则它将被关闭一个...
2021-03-14 13:21:13
我听说 i-- 可能比 --i 更快,因为在第二种情况下,处理器需要递减,然后针对新值进行测试(指令之间存在数据依赖性),而在第一种情况下,处理器可以测试现有值并在一段时间后递减该值。我不确定这是否适用于 JavaScript 或仅适用于非常低级的 C 代码。
2021-03-21 13:21:13
如果我没记错的话,我的硬件课程教授说,测试是否为 0 是最快的“计算”。在 while(i--) 中,测试始终是 0 的测试。也许这就是为什么这是最快的?
2021-04-02 13:21:13
很好 :) 但是没有测试反向“for”循环……但是 peirix 或 searlea 提到的 for 循环应该与以“i--”为条件的“while”循环几乎相同。那是测试过的最快的循环。
2021-04-11 13:21:13

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

我最近测试这个问题之前,括号中的以下想法我的信念:

[[对于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.popor array.shift如果你这样做,把它放在窗帘后面。最大矫枉过正是添加选项,forEach选择循环算法。]

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

这些库还可以将东西(多线程)放在背后,并且专业的程序员也会使它们保持最新状态。

我做了更多的审查,结果证明在 C/C++ 中,即使对于 5e9 = (50,000x100,000) 操作,如果测试是针对像@alestanis 所说的常量进行的,则上升和下降之间没有区别(JsPerf 结果有时不一致,但总的来说是一样的:你不能产生很大的不同。)
所以--i恰好是一个“豪华”的东西。它只会让你看起来像一个更好的程序员。:)

另一方面,对于在这种 5e9 情况下展开,它使我从 12 秒下降到 10 秒时的 2.5 秒,以及 20 秒时的 2.1 秒。它没有优化,而优化已将事情缩短到无法衡量的时间。:) (展开可以按照我上面的方式或使用 完成i++,但这并不能在 JavaScript 中取得进展。)

总而言之:保持i--/i++++i/i++与工作面试的差异,坚持array.forEach或其他复杂的库功能(如果可用)。;)

@jalf 确实如此,+1。不同的unlooping lenghts(>=1)在效率上是不同的。这就是为什么如果可能的话将这项工作留给图书馆更方便,更不用说浏览器在不同的架构上运行,所以如果他们决定如何做可能会更好array.each(...)我不认为他们会尝试尝试解开普通的 for 循环。
2021-03-20 13:21:13
关键词是“可以”。您展开的循环也可能比原始循环慢。优化时,始终进行测量,以便您准确了解更改产生的影响。
2021-03-21 13:21:13
然后你去 -+1给你一个Great Answer徽章。真的很棒的答案。:)
2021-03-25 13:21:13
APL/A++ 也不是一种语言。人们用这些来表达他们对语言细节不感兴趣,而是对这些语言共享的东西感兴趣。
2021-04-09 13:21:13
@BarnabasSzabolcs 这个问题专门针对 JavaScript,而不是 C 或其他语言。在 JS 中有区别的,请看下面我的回答。虽然不适用于该问题,+1 很好的答案!
2021-04-10 13:21:13

i-- 一样快 i++

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

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

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

附加变量将被通用解释器/编译器的优化器消除。重点是“与 0 比较” - 请参阅我的回答以获取进一步的解释。
2021-03-15 13:21:13
你在这里显然错了。现在循环控制需要额外的内部变量(i 和 up),并且取决于 JavaScript 引擎,这可能会限制优化代码的潜力。JIT 会将简单的循环转换为直接的机器操作码,但是如果循环有太多变量,那么 JIT 将无法优化得那么好。通常,它受 JIT 使用的体系结构或 cpu 寄存器的限制。初始化一次,然后简单地使用最干净的解决方案,这就是为什么到处都推荐它的原因。
2021-03-17 13:21:13
最好将 'up' 放在循环中: for (var i=0, up=Things.length; i<up; i++) {}
2021-03-23 13:21:13
为什么内部变量会产生问题?这是错误的,因为循环控制不会有一个“额外的变量”,因为up正是因为Things.length不通过引用传递(作为一个对象或阵列将是),但直接value。换句话说,它完全按照数字 10 或 100000 插入循环控件中。您只是将其重命名为循环外,因此没有任何区别。出于这个原因,答案本身是完全有效的。当看到 i < up 时,循环控制看到 i < 10 而不是 i <(对数字 10 的引用)。实际上up在它里面存储了一个原语,不要忘记这一点。
2021-04-04 13:21:13

因为您对该主题感兴趣,所以请查看 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 测试,我得到以下循环:

基准示例。

@dreamcash 在 Chrome 88.x 上,所有反向循环总是比所有正向循环快。jsben.ch/eng8bmeasurethat.net/Benchmarks/ShowResult/162677jsbench.me/5ykkzsysa9有时reverse-for,有时reverse-optimized-for,有时reverse-while。
2021-03-14 13:21:13
@johnywhy okey 我想念 okey,所以它仍然有一点不同。您会发布带有这些结果的答案吗?
2021-03-26 13:21:13