堆栈跟踪与TCO:非尾递归语言通常具有高效的堆栈帧吗?

时间:2013-09-01 00:25:25

标签: compiler-construction compiler-optimization

我听说某些语言不支持尾调用优化(TCO)的一个原因是this optimization comes at the cost of obfuscating the call stack if / when调试时应该要求查看堆栈跟踪。 (我听说过其他原因,比如“虚拟机不支持它”,但是现在让我们忽视Java。)

在某些情况下,对于TCO可能的某些语言,但是在没有执行TCO的情况下,堆栈帧的唯一目的是维护元数据,以便生成任何最终的堆栈跟踪。即,堆栈帧可以是最小的,仅包含足够的信息来生成堆栈跟踪。

问题:最小化此类堆栈帧的大小是否有意义?它不会最小化堆栈空间的使用,从而在耗尽空间之前允许更深层次的递归吗?是否尝试过这种思想适用的语言? (我正在考虑Python。)或者这是否真的节省了实际空间? (我认为生成漂亮的堆栈跟踪所需的元数据实际上与堆栈帧中的正常数据相比要大得多。)

简而言之:最大限度地减少堆栈帧的大小,以替代TCO。

PS。我的想法不是基于任何实际的基准。我可以离开这里。

2 个答案:

答案 0 :(得分:2)

如果正在调试,除非您正在调试优化本身,否则不需要打开所有优化。因此,尾部递归优化(TRO)不必在调试期间禁用回溯,除非开发环境是脑死亡(例如,没有“禁用优化”选项)。

元数据占用的空间,如果你确实记录了它,那么它很小;基本上它说,“递归召唤”。具有少量字节元素的链表可以解决问题。但我不认为在TRO面前记录回溯元数据是有意义的;如果你正在优化,你可能也不想支付记录元数据的额外时间成本,只是因为某人可能想要稍后调试。

我不确定的意思是“最小化堆栈帧的大小”,因为你在TRO的上下文中提出了问题。通常,良好的编译器将通过仅分配足够的空间来执行堆栈帧实例所表示的(子)例程的整个计算来自动地最小化大小。一种标准优化是让具有非重叠寿命的变量共享堆栈帧中的相同空间。可以通过几种不同的方式实现这一点:a)子程序体内不重叠的嵌套范围可以在类似堆栈的规则中轻松地共享它们的空间,并且b)处理“堆栈框架”而不是堆栈,但作为一堆用于存储溢出值的寄存器;注册着色algorthim可以很好地处理这个问题。

有时,堆栈帧最小化会因操作系统的(缺乏)支持而受损。有关Windows陷阱如何显着损坏堆栈帧大小的讨论,请参阅this

答案 1 :(得分:1)

您基本上可以在“优化”尾递归或维护“准确”调用堆栈之间进行选择。您要么维护某种“面包屑”并且不完全优化尾递归,要么在丢失堆栈跟踪时优化尾递归。

每个设计良好的语言实现(对运行时性能和“足迹”都感兴趣)“最小化”堆栈帧大小。

但是,就像计算中的几乎所有内容一样,在竞争因素之间需要权衡 - 性能,“足迹”,实现的简单性/可靠性,灵活性(例如,多种语言),可调试性等等。