我什么时候应该省略帧指针?

Pet*_*osB 28 c compiler-construction optimization

省略帧指针时是否有任何实质性的优化?如果我通过阅读理解正确的话这个页面,-fomit-frame-pointer在使用时我们要避免保存,设置和恢复帧指针.

这是仅针对每个函数调用完成的吗?如果是这样,是否真的值得为每个函数避免一些指令?优化不是一件容易的事.除调试限制外,使用此选项的实际含义是什么?

我使用和不使用此选项编译了以下C代码

int main(void)
{
        int i;

        i = myf(1, 2);
}

int myf(int a, int b)
{
        return a + b;
}
Run Code Online (Sandbox Code Playgroud)

,

# gcc -S -fomit-frame-pointer code.c -o withoutfp.s
# gcc -S code.c -o withfp.s
Run Code Online (Sandbox Code Playgroud)

.

diff -u 这两个文件显示以下汇编代码:


--- withfp.s    2009-12-22 00:03:59.000000000 +0000
+++ withoutfp.s 2009-12-22 00:04:17.000000000 +0000
@@ -7,17 +7,14 @@
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl   -4(%ecx)
-       pushl   %ebp
-       movl    %esp, %ebp
        pushl   %ecx
-       subl    $36, %esp
+       subl    $24, %esp
        movl    $2, 4(%esp)
        movl    $1, (%esp)
        call    myf
-       movl    %eax, -8(%ebp)
-       addl    $36, %esp
+       movl    %eax, 20(%esp)
+       addl    $24, %esp
        popl    %ecx
-       popl    %ebp
        leal    -4(%ecx), %esp
        ret
        .size   main, .-main
@@ -25,11 +22,8 @@
 .globl myf
        .type   myf, @function
 myf:
-       pushl   %ebp
-       movl    %esp, %ebp
-       movl    12(%ebp), %eax
-       addl    8(%ebp), %eax
-       popl    %ebp
+       movl    8(%esp), %eax
+       addl    4(%esp), %eax
        ret
        .size   myf, .-myf
        .ident  "GCC: (GNU) 4.2.1 20070719 
Run Code Online (Sandbox Code Playgroud)

有人可以阐明上面代码的关键点 - -fomit-frame-pointer确实有所作为?

编辑: objdump输出被替换为gcc -S's

Eri*_*nen 29

-fomit-frame-pointer允许一个额外的寄存器可用于通用目的.我认为这对32位x86来说真的很重要,因为寄存器有点缺乏.*

人们期望看到EBP不再在每次函数调用时保存和调整,并且可能在正常代码中使用EBP,并且在EBP用作通用寄存器的情况下更少的堆栈操作.

您的代码太简单了,无法从这种优化中获得任何好处 - 您没有使用足够的寄存器.此外,您还没有打开优化器,这可能是查看其中一些效果所必需的.

*ISA寄存器,而不是微架构寄存器.


Tho*_*ini 9

省略它的唯一缺点是调试要困难得多.

主要好处是有一个额外的通用寄存器可以对性能产生重大影响.显然,这个额外的寄存器仅在需要时使用(可能在你非常简单的函数中它不是); 在某些功能中,它比其他功能更有区别.

  • 他们错了.例如,`printf()`debugging(**IS**仍在调试)是非常可能的. (17认同)
  • 无论使用何种编译器选项,您仍然可以在指令(汇编语言)级别进行调试.并不像源代码级调试那么容易,但"不可能"绝对是错误的. (9认同)

Com*_*ger 7

通过使用-S参数输出程序集,您通常可以从GCC获得更有意义的汇编代码:

$ gcc code.c -S -o withfp.s
$ gcc code.c -S -o withoutfp.s -fomit-frame-pointer
$ diff -u withfp.s withoutfp.s
Run Code Online (Sandbox Code Playgroud)

GCC不关心地址,因此我们可以比较直接生成的实际指令.对于你的叶子功能,这给出:

 myf:
-       pushl   %ebp
-       movl    %esp, %ebp
-       movl    12(%ebp), %eax
-       addl    8(%ebp), %eax
-       popl    %ebp
+       movl    8(%esp), %eax
+       addl    4(%esp), %eax
    ret
Run Code Online (Sandbox Code Playgroud)

GCC不会生成将帧指针压入堆栈的代码,这会更改传递给堆栈上函数的参数的相对地址.


Tho*_*ews 5

分析您的程序,看是否有明显的不同。

接下来,描述您的开发过程。调试更容易或更困难吗?您是否花更多的时间进行开发?

不进行概要分析的优化浪费时间和金钱。