为了实现尾调用优化,jvm 必须牺牲什么?
-
06-07-2019 - |
题
人们说 clojure 实现非常出色,除了没有尾部调用优化的限制——这是 jvm 的限制,而不是 clojure 实现的限制。
http://lambda-the-ultimate.org/node/2547
有人说,将 TCO 实现到 Python 中会牺牲
- 堆栈跟踪转储,以及
- 调试规律性。
向我解释一下尾部调用优化的重要意义是什么以及为什么 Python 需要它
对于 TCO 的 jvm 实现是否也必须做出同样的牺牲?还需要牺牲什么吗?
解决方案
虽然有所不同(因为 il 指令已经存在),但值得注意的是 .Net 的额外工作 64位JIT团队 必须经历尊重所有尾部调用。
我特别指出评论:
当然,不利的一面是,如果您必须调试或分析优化的代码,请准备好处理看起来缺少一些帧的调用堆栈。
我认为 JVM 也不太可能避免这种情况。
鉴于此,在请求尾部调用优化的情况下,JIT 应该假设它是 必需的 为了避免堆栈溢出,这不能在调试版本中关闭。如果它们在您到达有趣的部分之前就崩溃了,那么它们对于调试就没有多大用处。“优化”实际上是一项永久功能,也是受其影响的堆栈跟踪的问题。
值得指出的是,在执行程序员概念上描述/理解为堆栈操作(例如调用函数)的操作时,任何避免创建真实堆栈帧的优化都会固有地导致呈现给用户的内容之间的脱节当调试/提供堆栈跟踪和现实时。
这是不可避免的,因为描述操作的代码与执行操作的状态机的机制越来越分离。
其他提示
是的,通常情况下,实施 TCO 会阻止您获得完整的堆栈跟踪。这是不可避免的,因为 TCO 的重点是避免创建额外的堆栈帧。
还值得注意的是,Clojure 有一个非堆栈消耗的“recur”功能,可以绕过当前 JVM 版本的这一限制。
例子:
(defn triangle [n accumulator]
(if
(<= n 0)
accumulator
(recur (dec n) (+ n accumulator))))
(triangle 1000000 0)
=> 500000500000 (note stack does not explode here!)
不隶属于 StackOverflow