Sou*_*ink 61 javascript performance loops
在他的书" 更快的网站"中, 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]);
}
Run Code Online (Sandbox Code Playgroud)
这对于for循环,do-while循环和while循环几乎相同.
我想知道,这是什么原因?为什么要这么快地递减迭代器呢?(我对此技术背景感兴趣,而不是在证明此声明的基准测试中感兴趣.)
编辑:乍一看这里使用的循环语法看起来不对.没有length-1或者i>=0,让我们澄清(我很困惑太).
这是循环语法的一般:
for ([initial-expression]; [condition]; [final-expression])
statement
Run Code Online (Sandbox Code Playgroud)
初始表达 -var i=length
首先评估此变量声明.
条件 -i--
在每次循环迭代之前计算此表达式.它将在第一次通过循环之前递减变量.如果此表达式求值为false循环结束.在JavaScript中是0 == false这样的,如果i最终等于0它被解释为false并且循环结束.
最终表达
在每次循环迭代结束时(在下一次条件评估之前)评估该表达式.这里不需要它是空的.所有三个表达式在for循环中都是可选的.
for循环语法不是问题的一部分,但因为它有点不常见,我认为澄清它是有趣的.也许有一个原因是它更快,因为它使用较少的表达式(0 == false"技巧").
Mik*_*vey 65
我不确定Javascript,在现代编译器下它可能没关系,但在"过去的日子"这段代码:
for (i = 0; i < n; i++){
.. body..
}
Run Code Online (Sandbox Code Playgroud)
会产生
move register, 0
L1:
compare register, n
jump-if-greater-or-equal L2
-- body ..
increment register
jump L1
L2:
Run Code Online (Sandbox Code Playgroud)
而后向计数代码
for (i = n; --i>=0;){
.. body ..
}
Run Code Online (Sandbox Code Playgroud)
会产生
move register, n
L1:
decrement-and-jump-if-negative register, L2
.. body ..
jump L1
L2:
Run Code Online (Sandbox Code Playgroud)
所以在循环中它只做两个额外的指令而不是四个.
djd*_*d87 27
我相信原因是因为你将循环结束点与0进行比较,这比再次比较< length(或另一个JS变量)要快 .
这是因为序数运算符<, <=, >, >=是多态的,因此这些运算符需要对运算符的左侧和右侧进行类型检查,以确定应该使用哪种比较行为.
这里有一些非常好的基准测试:
Gum*_*mbo 15
很容易说迭代可以有更少的指令.我们来比较这两个:
for (var i=0; i<length; i++) {
}
for (var i=length; i--;) {
}
Run Code Online (Sandbox Code Playgroud)
当您将每个变量访问和每个运算符计为一条指令时,前一个for循环使用5条指令(读取i,读取length,评估i<length,测试(i<length) == true,增量i),而后者仅使用3条指令(读取i,测试i == true,递减i).这是5:3的比例.
那么使用反向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]);
}
Run Code Online (Sandbox Code Playgroud)
IMO这个至少比一个更易读的代码 for(i=length; i--;)
我对 C# 和 C++(类似语法)进行了基准测试。实际上,与或for相比,循环的性能本质上有所不同。在 C++ 中,递增时性能会更好。它还可能取决于编译器。do whilewhile
我认为在 Javascript 中,这一切都取决于浏览器(Javascript 引擎),但这种行为是可以预料的。Javascript 针对 DOM 进行了优化。因此,想象一下,您循环遍历每次迭代时获得的 DOM 元素集合,并在必须删除它们时递增计数器。您删除了该0元素,然后删除1了该元素,但随后跳过了取代 的元素0。当向后循环时,这个问题就消失了。我知道给出的示例不仅仅是正确的,但我确实遇到过必须从不断变化的对象集合中删除项目的情况。
因为向后循环比前向循环更常见,所以我猜测 JS 引擎就是为此而优化的。
| 归档时间: |
|
| 查看次数: |
27715 次 |
| 最近记录: |