[英]Why is recursion faster than a flat for loop for a summation function on JavaScript?
I'm working in a language that translates to JavaScript. 我正在使用一种翻译成JavaScript的语言。 In order to avoid some stack overflows, I'm applying tail call optimization by converting certain functions to for loops.
为了避免一些堆栈溢出,我通过将某些函数转换为for循环来应用尾调用优化。 What is surprising is that the conversion is not faster than the recursive version.
令人惊讶的是,转换并不比递归版本快。
http://jsperf.com/sldjf-lajf-lkajf-lkfadsj-f/5 http://jsperf.com/sldjf-lajf-lkajf-lkfadsj-f/5
Recursive version: 递归版:
(function recur(a0,s0){
return a0==0 ? s0 : recur(a0-1, a0+s0)
})(10000,0)
After tail call optimization: 尾部调用优化后:
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;
After some cleanup using Google Closure Compiler: 使用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;
The recursive version is faster than the "optimized" ones! 递归版本比“优化”版本更快! How can this be possible, if the recursive version has to handle thousands of context changes?
如果递归版本必须处理数以千计的上下文更改,那怎么可能呢?
There's more to optimising than tail-call optimisation. 优化比尾部调用优化更多。
For instance, I notice you're using two temporary variables, when all you need is: 例如,我注意到你正在使用两个临时变量,只需要:
s2 += a1;
a1--;
This alone practically reduces the number of operations by a third, resulting in a performance increase of 50% 仅这一点实际上将操作次数减少了三分之一,从而使性能提高了50%
In the long run, it's important to optimise what operations are being performed before trying to optimise the operations themselves. 从长远来看,在尝试优化操作本身之前优化正在执行的操作非常重要。
as Kolink say what your piece of code do is simply adding n
to the total, reduce n
by 1, and loop until n
not reach 0
正如Kolink所说,你的代码所做的只是在总数中加
n
,将n
减1,然后循环直到n
不到0
so just do that : 所以就这样做:
n = 10000, o = 0; while(n) o += n--;
it's more faster and lisible than the recursive version, and off course output the same result 它比递归版本更快更快 ,并且当然输出相同的结果
There are not so much context changes inside the recursive version as you expect, since the named function recur
is contained in the scope of recur
itself/they share the same scope. 递归版本中的上下文变化并不像您期望的那么多,因为命名函数
recur
包含在recur
本身的范围内/它们共享相同的范围。 The reason for that has to do with the way the JavaScript engines evaluate scope, and there are plenty of websites out there which explain this topic, so I will not do it here. 其原因与JavaScript引擎评估范围的方式有关,并且有很多网站可以解释这个主题,所以我不会在这里做。 At a second look you will notice that
recur
is also a so called "pure" function, which basically means it never has to leave it's own scope as long as the internal execution runs (simply put: until it returns a value). 再看看你会注意到
recur
也是一个所谓的“纯”函数,这基本上意味着只要内部执行运行它就永远不会离开它自己的范围(简单地说:直到它返回一个值)。 These two facts make it basically fast. 这两个事实使它基本上很快。 I just want to mention here, the first example is the only tail call optimized one of all three – a tc optimization can only be done in recursive functions and this is the only recursive one.
我只想在这里提一下,第一个例子是所有三个中唯一的尾部调用优化 - tc优化只能在递归函数中完成,这是唯一的递归函数。
However, a second look at the second example (no pun intended) reveals, that the "optimizer" made things worse for you, since it introduced scopes into the former pure function by splitting the operation into 然而,第二个看第二个例子(没有双关语)揭示了“优化器”使你的事情变得更糟,因为它通过将操作分成以前的操作将范围引入到前一个纯函数中
while
loop while
循环 Which leads to poorer performance since now the engine has to handle 10000 context changes. 这导致性能较差,因为现在引擎必须处理10000个上下文更改。
To tell you the truth I do not know why the third example is poorer in performance than the recursive one, so maybe it has to do with: 说实话我不知道为什么第三个例子的性能比递归的差,所以它可能与:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.