![](/img/trans.png)
[英]Why is this loop in a then callback faster than in an async function?
[英]Why is recursion faster than a flat for loop for a summation function on JavaScript?
我正在使用一种翻译成JavaScript的语言。 为了避免一些堆栈溢出,我通过将某些函数转换为for循环来应用尾调用优化。 令人惊讶的是,转换并不比递归版本快。
http://jsperf.com/sldjf-lajf-lkajf-lkfadsj-f/5
递归版:
(function recur(a0,s0){
return a0==0 ? s0 : recur(a0-1, a0+s0)
})(10000,0)
尾部调用优化后:
ret3 = void 0;
a1 = 10000;
s2 = 0;
(function(){
while (!ret3) {
a1 == 0
? ret3 = s2
: (a1_tmp$ = a1 - 1 ,
s2_tmp$ = a1 + s2,
a1 = a1_tmp$,
s2 = s2_tmp$);
}
})();
ret3;
使用Google Closure Compiler进行一些清理之后:
ret3 = 0;
a1 = 1E4;
for(s2 = 0; ret3 == 0;)
0 == a1
? ret3 = s2
: (a1_tmp$ = a1 - 1 ,
s2_tmp$ = a1 + s2,
a1 = a1_tmp$,
s2 = s2_tmp$);
c=ret3;
递归版本比“优化”版本更快! 如果递归版本必须处理数以千计的上下文更改,那怎么可能呢?
优化比尾部调用优化更多。
例如,我注意到你正在使用两个临时变量,只需要:
s2 += a1;
a1--;
仅这一点实际上将操作次数减少了三分之一,从而使性能提高了50%
从长远来看,在尝试优化操作本身之前优化正在执行的操作非常重要。
编辑:这是一个更新的jsperf
递归版本中的上下文变化并不像您期望的那么多,因为命名函数recur
包含在recur
本身的范围内/它们共享相同的范围。 其原因与JavaScript引擎评估范围的方式有关,并且有很多网站可以解释这个主题,所以我不会在这里做。 再看看你会注意到recur
也是一个所谓的“纯”函数,这基本上意味着只要内部执行运行它就永远不会离开它自己的范围(简单地说:直到它返回一个值)。 这两个事实使它基本上很快。 我只想在这里提一下,第一个例子是所有三个中唯一的尾部调用优化 - tc优化只能在递归函数中完成,这是唯一的递归函数。
然而,第二个看第二个例子(没有双关语)揭示了“优化器”使你的事情变得更糟,因为它通过将操作分成以前的操作将范围引入到前一个纯函数中
while
循环 这导致性能较差,因为现在引擎必须处理10000个上下文更改。
说实话我不知道为什么第三个例子的性能比递归的差,所以它可能与:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.