我是一名汇编语言的初学者,注意到编译器生成的x86代码即使在发布/优化模式下也保留了帧指针(EBP
寄存器),即使可以将其用于其他用途。
我明白帧指针可能会使代码更易于调试,并且如果在函数中调用alloca()
可能是必需的。 但是,x86只有非常少的寄存器,使用其中两个来保存堆栈帧的位置,而一个寄存器就足够,这对我来说没有意义。为什么在优化/发布构建中省略帧指针被认为是一个坏主意?
我是一名汇编语言的初学者,注意到编译器生成的x86代码即使在发布/优化模式下也保留了帧指针(EBP
寄存器),即使可以将其用于其他用途。
我明白帧指针可能会使代码更易于调试,并且如果在函数中调用alloca()
可能是必需的。 但是,x86只有非常少的寄存器,使用其中两个来保存堆栈帧的位置,而一个寄存器就足够,这对我来说没有意义。为什么在优化/发布构建中省略帧指针被认为是一个坏主意?
.eh_frame_hdr
这个区段也用于运行时的异常处理。你可以在大多数 Linux 系统的二进制文件中(使用 objdump -h
命令)找到它。对于 /bin/bash
,它大约是 16k,对于 GNU /bin/true
,它只有 572B,对于 ffmpeg
,它是 108k。GCC 提供了禁用这个区段生成的选项,但它是一个“普通”的数据区段,而不是 strip
默认删除的调试区段。否则,如果一个库函数没有调试符号,你将无法通过回溯定位其位置。这个区段可能比它所替代的 push/mov/pop
指令更加庞大,但它几乎没有运行时成本(例如微操作缓存)。 - Peter Cordes在这些已经很好的回答中,我想再添砖加瓦。
在一个良好的语言体系结构中,有一个堆栈帧链是必要的。BP指向当前帧,其中存储子例程本地变量。(局部变量在负偏移处,参数在正偏移处。)
认为它阻止了优化中可用的寄存器的观点引发了一个问题:何时何地进行优化才是值得的呢?
仅当紧密循环不调用函数且程序计数器花费其时间的重大部分在编译器实际上会看到的代码中(即非库函数)时,优化才是值得的。这通常只占整个代码的一小部分,特别是在大型系统中。
其他代码可以被改变和挤压以消除周期,但它实际上并没有什么影响,因为程序计数器几乎从来不在那里。
我知道你没有提出这个问题,但在我的经验中,99%的性能问题与编译器优化完全无关,而与过度设计有关。
这取决于编译器,我曾看到x86编译器生成的优化代码自由使用EBP寄存器作为通用寄存器。(尽管我不记得是哪个编译器了。)
编译器可能还会选择保留EBP寄存器来协助堆栈展开处理异常,但这也取决于具体的编译器实现。
-fomit-frame-pointer
(如果ABI允许的话)。我记得GCC、clang、ICC和MSVC都这样做,即使是针对32位Windows。是的,我的回答为什么使用ebp而不是esp寄存器来定位堆栈上的参数?表明即使是32位Windows也可以省略帧指针。32位x86 Linux肯定可以并且已经这样做了。当然,64位ABI从一开始就允许省略帧指针。 - Peter Cordes在任何现代硬件上,使用堆栈帧已经变得非常便宜。如果您有廉价的堆栈帧,则保存几个寄存器并不重要。我相信快速堆栈帧与更多寄存器之间是一种工程权衡,而快速堆栈帧胜出了。
通过纯粹的寄存器,您可以节省多少?这值得吗?