JavaScript 循环性能 - 为什么将迭代器递减到 0 比递增更快
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 
原文地址: http://stackoverflow.com/questions/3520688/
Warning: these are provided under cc-by-sa 4.0 license.  You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JavaScript loop performance - Why is to decrement the iterator toward 0 faster than incrementing
提问by Soundlink
In his book Even Faster Web SitesSteve Sounders writes that a simple way to improve the performance of a loop is to decrement the iterator toward 0 rather than incrementing toward the total length (actually the chapter was written by Nicholas C. Zakas). This change can result in savings of up to 50% off the original execution time, depending on the complexity of each iteration. For example:
Steve Sounders在他的《甚至更快的网站》一书中写道,提高循环性能的一个简单方法是将迭代器递减到 0,而不是朝着总长度递增(实际上这一章是由 Nicholas C. Zakas 编写的)。根据每次迭代的复杂性,此更改最多可将原始执行时间节省 50%。例如:
var values = [1,2,3,4,5];
var length = values.length;
for (var i=length; i--;) {
   process(values[i]);
}
This is nearly identical for the forloop, the do-whileloop, and the whileloop.
这对于for循环、do-while循环和while循环几乎是相同的。
I'm wondering, what's the reason for this? Why is to decrement the iterator so much faster?(I'm interested in the technical background of this and not in benchmarks proving this claim.)
我想知道,这是什么原因?为什么要更快地减少迭代器?(我对此的技术背景感兴趣,而不是对证明此声明的基准感兴趣。)
EDIT: At first sight the loop syntax used here looks wrong. There is no length-1or i>=0, so let's clarify (I was confused too).
编辑:乍一看,这里使用的循环语法看起来是错误的。没有length-1or i>=0,所以让我们澄清一下(我也很困惑)。
Here is the general for loop syntax:
这是一般的 for 循环语法:
for ([initial-expression]; [condition]; [final-expression])
   statement
- initial-expression- - var i=length- This variable declaration is evaluated first. 
- condition- - i--- This expression is evaluated before each loop iteration. It will decrement the variable before the first pass through the loop. If this expression evaluates to - falsethe loop ends. In JavaScript is- 0 == falseso if- ifinally equals- 0it is interpreted as- falseand the loop ends.
- final-expression - This expression is evaluated at the end of each loop iteration (before the next evaluation of condition). It's not needed here and is empty. All three expressions are optional in a for loop. 
- 初始表达式- - var i=length- 首先评估此变量声明。 
- 条件—— - i--- 该表达式在每次循环迭代之前计算。它将在第一次通过循环之前递减变量。如果此表达式的计算结果为 - false循环结束。在 JavaScript 中- 0 == false,如果- ifinally 等于- 0它被解释为- false循环结束。
- 最终表达式 - 该表达式在每次循环迭代结束时进行计算(在下一次计算condition 之前)。这里不需要它,它是空的。所有三个表达式在 for 循环中都是可选的。 
The for loop syntax is not part of the question, but because it's a little bit uncommon I think it's interesting to clarify it. And maybe one reason it's faster is, because it uses less expressions (the 0 == false"trick").
for 循环语法不是问题的一部分,但因为它有点不常见,我认为澄清它很有趣。也许它更快的一个原因是,因为它使用的表达式更少(0 == false“技巧”)。
回答by Mike Dunlavey
I'm not sure about Javascript, and under modern compilers it probably doesn't matter, but in the "olden days" this code:
我不确定 Javascript,在现代编译器下它可能无关紧要,但在“过去”这段代码:
for (i = 0; i < n; i++){
  .. body..
}
would generate
会产生
move register, 0
L1:
compare register, n
jump-if-greater-or-equal L2
-- body ..
increment register
jump L1
L2:
while the backward-counting code
而向后计数的代码
for (i = n; --i>=0;){
  .. body ..
}
would generate
会产生
move register, n
L1:
decrement-and-jump-if-negative register, L2
.. body ..
jump L1
L2:
so inside the loop it's only doing two extra instructions instead of four.
所以在循环内部它只执行两个额外的指令而不是四个。
回答by djdd87
I believe the reason is because you're comparing the loop end point against 0, which is faster then comparing again  < length(or another JS variable).
我相信原因是因为您将循环终点与 0 进行比较,这比再次比较< length(或另一个 JS 变量)要快  。
It is because the ordinal operators <, <=, >, >=are polymorphic, so these operators require type checks on both left and right sides of the operator to determine what comparison behaviour should be used.
正是因为序数运算符<, <=, >, >=是多态的,所以这些运算符需要在运算符的左右两侧进行类型检查,以确定应该使用什么比较行为。
There's some very good benchmarks available here:
这里有一些非常好的基准测试:
回答by Gumbo
It is easy to say that an iteration can have less instructions. Let's just compare these two:
很容易说一次迭代可以有更少的指令。让我们来比较一下这两个:
for (var i=0; i<length; i++) {
}
for (var i=length; i--;) {
}
When you count each variable access and each operator as one instruction, the former forloop uses 5 instructions (read i, read length, evaluate i<length, test (i<length) == true, increment i) while the latter uses just 3 instructions (read i, test i == true, decrement i). That is a ratio of 5:3.
当您将每个变量访问和每个运算符算作一条指令时,前一个for循环使用 5 条指令(read i、read length、evaluate i<length、test (i<length) == true、increment i),而后者仅使用 3 条指令(read i、test i == true、decrement i)。那是5:3的比例。
回答by Marco Demaio
What about using a reverse while loop then:
那么使用反向while循环怎么样:
var values = [1,2,3,4,5]; 
var i = values.length; 
/* i is 1st evaluated and then decremented, when i is 1 the code inside the loop 
   is then processed for the last time with i = 0. */
while(i--)
{
   //1st time in here i is (length - 1) so it's ok!
   process(values[i]);
}
IMO this one at least is a more readble code than for(i=length; i--;)
IMO 这至少是一个比 for(i=length; i--;)
回答by Estus Flask
forincrement vs. decrement in 2017
for2017年增减
In modern JS engines incrementing in forloops is generally faster than decrementing (based on personal Benchmark.js tests), also more conventional:
在现代 JS 引擎中,for循环递增通常比递减更快(基于个人 Benchmark.js 测试),也更传统:
for (let i = 0; i < array.length; i++) { ... }
It depends on the platform and array length if length = array.lengthhas any considerable positive effect, but usually it doesn't:
如果length = array.length有任何相当大的积极影响,它取决于平台和数组长度,但通常不会:
for (let i = 0, length = array.length; i < length; i++) { ... }
Recent V8 versions (Chrome, Node) have optimizations for array.length, so length = array.lengthcan be efficiently omitted there in any case.
最近的 V8 版本(Chrome、Node)对 进行了优化array.length,因此length = array.length在任何情况下都可以有效地省略。
回答by icanhazstring
There is an even more "performant" version of this. Since each argument is optional in for loops you can skip even the first one.
还有一个更“高性能”的版本。由于每个参数在 for 循环中都是可选的,因此您甚至可以跳过第一个。
var array = [...];
var i = array.length;
for(;i--;) {
    do_teh_magic();
}
With this you skip even the check on the [initial-expression]. So you end up with just one operation left.
有了这个,您甚至可以跳过[initial-expression]. 所以你最终只剩下一个手术了。
回答by nabrown
I've been exploring loop speed as well, and was interested to find this tidbit about decrementing being faster than incrementing. However, I have yet to find a test that demonstrates this. There are lots of loop benchmarks on jsperf. Here is one that tests decrementing:
我也一直在探索循环速度,并且有兴趣找到这个关于递减比递增更快的花絮。但是,我还没有找到证明这一点的测试。jsperf 上有很多循环基准测试。这是测试递减的一个:
http://jsperf.com/array-length-vs-cached/6
http://jsperf.com/array-length-vs-cached/6
Caching your array length, however (also recommended Steve Souders' book) does seem to be a winning optimization.
然而,缓存你的数组长度(也推荐 Steve Souders 的书)似乎是一个成功的优化。
回答by BenG
in modern JS engines, the difference between forward and reverse loops is almost non-existent anymore. But the performance difference comes down to 2 things:
在现代 JS 引擎中,正向循环和反向循环之间的区别几乎不再存在。但性能差异归结为两件事:
a) extra lookup every of length property every cycle
a) 每个周期额外查找每个长度属性
//example:
    for(var i = 0; src.length > i; i++)
//vs
    for(var i = 0, len = src.length; len > i; i++)this is the biggest performance gain of a reverse loop, and can obviously be applied to forward loops.
这是反向循环的最大性能增益,显然可以应用于正向循环。
b) extra variable assignment.
b) 额外的变量赋值。
the smaller gain of a reverse loop is that it only requires one variable assignment instead of 2
反向循环的较小增益是它只需要一个变量分配而不是 2
//example:
    var i = src.length; while(i--)回答by naikus
I am not sure if it's faster but one reason i see is that when you iterate over an array of large elements using increment you tend to write:
我不确定它是否更快,但我看到的一个原因是,当您使用增量迭代大型元素数组时,您倾向于编写:
for(var i = 0; i < array.length; i++) {
 ...
}
You are essentially accessing the length property of the array N (number of elements) times. Whereas when you decrement, you access it only once. That could be a reason.
您实际上是在访问数组 N(元素数)的长度属性。而当您递减时,您只能访问它一次。这可能是一个原因。
But you can also write incrementing loop as follows:
但是你也可以写如下递增循环:
for(var i = 0, len = array.length; i < len; i++) {
 ...
}
回答by Crashworks
Have you timed it yourself? Mr. Sounders might be wrong with regards to modern interpreters. This is precisely the sort of optimization in which a good compiler writer can make a big difference.
你自己计时了吗?关于现代口译员,Sounders 先生可能是错误的。这正是优秀的编译器编写者可以发挥重大作用的优化类型。

