无堆栈分配整个程序编译

No stack allocation whole program compilation?

本文关键字:程序 编译 堆栈 分配      更新时间:2023-10-16

如果你编写的应用程序是:

  • 单线程
  • 调用图中没有周期
  • 不使用 alloca 或 VLA

现代的整个程序优化编译器可以优化所有堆栈分配(例如GCC,MSVC,ICC)吗?似乎在这些情况下,它应该能够静态分配所有可能的堆栈空间。通过"整个程序",我的意思是编译器可以访问/all/源代码(在运行时没有dlopen'ing东西的可能性等)。

如果您可以保证您所说的条件,那么是的:可以有效地完全静态分配堆栈。每个函数都有一个堆栈内存块。

但是,实际的编译器会这样做吗?不。

这样做绝对没有任何好处。事实上,它可能比一无所获。通常,大部分工作堆栈都在缓存中,因此对其进行修改非常便宜。如果堆栈位于静态内存中,则只有在您最近调用该函数时,才会缓存任何特定函数的"堆栈"内存。使用真实堆栈,您更有可能在缓存中工作。

此外,为每个函数提供一个堆栈内存块可以很容易地使程序的静态内存使用量远远大于它需要的。堆栈是固定大小的构造;无论您有多少功能,堆栈都会占用一定的大小。如果您有 100,000 个函数,每个函数占用 64 字节的空间,那么您的静态"堆栈"必须占用 ~6.4MB 的空间。

为什么?您永远不会在任何时候使用大部分内存。该程序可以在 1MB 甚至 512KB 的堆栈中正常运行;为什么要白白占用 6 倍的内存?

因此,它既不是性能优化,又会使程序内存膨胀。

这是一个太长的评论,不能成为评论:

请注意,虽然理论上所有堆栈分配都可以优化,但分配的堆栈可能会超过必要的数量。这不是OP所要求的,但考虑一下可能会很有趣。找到所需的最小大小分配等同于解决停止问题。想象一个程序的结构如下:

<do 'something'>
<call last thing which happens to require more
 stack space than everything else in 'something'>

仅当<do 'something'>"停止"时,才需要额外的堆栈空间。

您还可以想象优化变得任意困难的其他变体。例如,您的程序可以简单地使用用户输入评估 3SAT 表达式,并据此执行某些操作 - 但该 3SAT 表达式可能具有也可能没有任何导致 true 的值。

也许有一个更微不足道的情况:用户可能永远不会输入需要更多堆栈空间进行处理的输入。

编译器可以

这样做,但这是一个特定的优化,它可能不会。

如果你有一个完全内联的程序,你将负责为函数调用设置堆栈帧的开销。

但是,如果您还想摆脱局部变量的堆栈分配,编译器必须将这些局部变量转换为全局变量。据我所知,没有编译器可以做到这一点,在某些平台上,与局部变量相比,引用全局变量需要额外的指令(因为地址必须加载两条指令而不是一条指令)。另外,由于引用堆栈变量是一项常见的操作,因此它通常被编码为较小的指令。

除非您将"不使用任何外部库"添加到列表中,否则对外部函数的任何调用都需要堆栈设置,因为它们的编译期望调用代码以特定方式传递其参数,最有可能在堆栈上。此外,这些库几乎肯定必须为自己的本地人调整堆栈。

此外,根据您的精确应用程序,即使您知道堆栈可以静态分配,编译器可能也很难知道您的代码中没有任何回调等,这会导致需要堆栈分配。

我只是看不到编译会尝试这种优化的情况,因为分配堆栈空间已经非常快了(我相信有几个寄存器操作)