递归的尾调用是否有性能优化
本文最后更新于:2022年4月22日 上午
递归的尾调用是否有性能优化?
我们在网络上看到 js 的性能优化的方式中,经常会看到函数的尾调用,那么实际是否有优化呢,查了对应的资料后发现,虽然在 es6 的标准中已经提出了尾调用优化(PTC),但是目前除了 safari
浏览器实现了之外,其它浏览器的引擎均未实现
示例:
function f(n) {
if (n <= 0) {
return 'foo';
}
return f(n - 1);
}
console.log(f(1000000) === 'foo');
追其原因,chrome
的js
引擎 v8
团队在2016年的时候发布了一遍博客 ES2015, ES2016, and beyond,这篇博客指出了尾调用存在的两个问题
很难辨别哪些函数是在正确的尾调用
尾调用会清空堆栈帧,会丢失有关执行流程的信息
    - 由于堆栈帧不连续性,很难在调试期间确认当前在某一个点
    - error.stack 中只有较少的执行流程信息,这可能会在分析客户端错误时对错误收集产生破坏
通过 shadow stack 可以实现查看堆栈信息,它内部会复制一份当前维护的堆栈信息,最大可以显示 128 条调用栈信息。但是依旧可能会产生开发和生产环境不一致的问题,且在性能方面过于昂贵,但当时v8
团队其实是实现了(PTC
)的,只需要在 执行的时候加上 --harmony-tailcalls
或--harmony-explicit-tailcalls
,但是后面将该补丁进行了移除
You can test out each version in the meantime by using the V8 flags
--harmony-tailcalls
and--harmony-explicit-tailcalls
. Update: These flags have been removed.
The tail call implementation is hidden behind the
--harmony-tailcalls
flag, which is off-by-default (and has been unstaged since February).
It is known to be broken in a variety of cases, including clusterfuzz
security issues (see sample Chromium issues below). To avoid letting
the implementation bitrot further on trunk, this patch removes it.
出于以上原因,v8
拒绝实现该尾调用,以至于到目前对于该标准的实现都处于僵局的情况,但是v8
提出了一种新的显示指定尾调用的方式 (STC),且得到了 Mozilla
和 Microsoft
的委员会成员共同支持,但是 Apple
处于反对状态。
它的示例如下,在需要使用尾调用的时候 通过 return continue
关键字,会显示的开启尾调用,不会新增新的堆栈,并且还会检查是否有语法错误,(例如:return continue 1 + foo()
) 就会报出语法错误
function factorial(n, acc = 1) {
if (n === 1) {
return acc;
}
return continue factorial(n - 1, acc * n)
}
总结
所以,就目前来说,只有 Safari
实现了尾递归的优化,其它浏览器都没有对其进行支持
参考资料
https://kangax.github.io/compat-table/es6/
https://v8.dev/blog/modern-javascript
https://es6.ruanyifeng.com/#docs/function#尾调用优化
https://github.com/tc39/proposal-ptc-syntax
https://chromium.googlesource.com/v8/v8.git/+/1769f892cef0822e6a8b5334e2ad909a0c33e906
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处。