You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript中循环与CPS循环的诡异性能表现探究

解析JavaScript CPS循环与常规循环的反常性能结果

哇,这个测试结果确实有点颠覆预期,我来帮你拆解几个关键原因:

1. Firefox引擎的尾调用优化(TCO)救了朴素函数循环

你担心朴素函数调用会导致栈空间线性增长,但Firefox的SpiderMonkey引擎对尾递归调用有非常成熟的优化。看你的NonTrampLoop代码:

  • n1里最后调用n3(i)或n2(i),都是尾调用(函数最后一步就是调用另一个函数,没有后续操作)
  • n2最后调用n3(i),n3最后调用n1(i),全都是尾调用结构

引擎会把这种尾递归转换成类似goto的跳转,完全不会增长调用栈,相当于把递归变成了隐式的循环。这种情况下,朴素函数循环的性能自然不会差,甚至因为引擎对这种规整的函数调用序列优化更到位,表现超出预期。

2. 蹦床循环的优化空间比你想象的大

你的TrampolineStreamlined版本用了一个共享的对象th来传递状态,而不是每次创建新的匿名函数:

  • 避免了频繁的堆上函数对象分配和垃圾回收
  • 引擎可以把th的属性访问优化成局部变量(比如var i = th.i),减少属性查找开销

相比之下,常规的for循环虽然直观,但引擎需要处理循环变量的边界检查、增量操作的各种情况;而蹦床的结构更规整,引擎更容易做内联优化,甚至把整个蹦床循环转换成无开销的跳转,所以在Firefox里反而跑的最快。

至于TrampolineSimplistic版本,虽然每次返回匿名函数,但Firefox的引擎可能对这些短生命周期的函数做了逃逸分析,把它们分配在栈上而不是堆上,减少了垃圾回收的压力,所以性能也没拉胯。

3. while(true)循环的优化瓶颈

你写的WhileTrueLoop用了while(true)加break的结构,这种写法反而给引擎的循环优化制造了麻烦:

  • 循环条件是常量true,引擎很难预测循环的退出时机,无法提前做循环展开、边界检查消除等优化
  • 里面的continue和break跳转逻辑比较零散,破坏了循环的规整性,引擎无法把循环转换成更高效的形式

而标准for循环的结构非常清晰,引擎可以轻松识别出这是一个固定次数的循环,直接做循环展开、常量折叠等优化,所以性能反而比while(true)好很多。

4. 测试代码的潜在影响

虽然所有测试都调用了console.log,但不同循环中console.log的调用时机和上下文可能影响引擎的优化。不过因为所有测试都包含相同的console.log逻辑,这部分的影响应该是相对一致的,不是导致结果反常的核心原因。

总结一下:不是你的测试有大疏漏,而是Firefox的引擎对尾递归和规整的函数调用结构优化太激进,反而让CPS风格的循环跑出了比常规循环更好的性能。

内容的提问来源于stack exchange,提问作者Nick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:45:38