避免gccfunction序幕开销?

我最近遇到了很多函数,其中gcc在x86上生成了非常糟糕的代码。 它们都符合以下模式:

if (some_condition) { /* do something really simple and return */ } else { /* something complex that needs lots of registers */ } 

将简单的情况看作是一个非常小的东西,以至于一半或更多的工作花在推动和弹出不会被修改的寄存器上。 如果我手动编写asm,我会在复杂的情况下保存并恢复已保存的跨调用寄存器,并且在简单的情况下完全避免触及堆栈指针。

有没有什么方法可以让gcc变得更聪明并且自己做到这一点? 最好使用命令行选项,而不是源代码中的丑陋黑客…

编辑:为了使它具体化,这里有一些非常接近我正在处理的一些函数:

 if (buf->pos end) { return *buf->pos++; } else { /* fill buffer */ } 

另一个:

 if (!initialized) { /* complex initialization procedure */ } return &initialized_object; 

另一个:

 if (mutex->type == SIMPLE) { return atomic_swap(&mutex->lock, 1); } else { /* deal with ownership, etc. */ } 

编辑2:我应该首先提到:这些函数不能内联。 他们有外部链接,他们是图书馆代码。 允许它们在应用程序中内联将导致各种问题。

也许升级你的gcc版本? 4.6刚刚发布。 据我了解,它有“部分内联”的可能性。 也就是说,函数的易于集成的外部部分被内联并且昂贵的部分被转换成调用。 但我必须承认,我自己并没有尝试过。

编辑:我从ChangeLog中引用的语句:

现在支持并启用部分内联,默认情况下为-O2和更高。 可以通过-fpartial-inlining控制该function。

部分内联拆分函数使用短热路径返回。 这允许更积极地内联热路径, 从而导致更好的性能并且通常减少代码大小 (因为function的冷部分不重复)。

在优化大小时(无论是在程序的冷区域还是在使用-Os进行编译时)的内联都得到了改进,以便更好地处理具有更大抽象代价的C ++程序,从而实现更小更快的代码。

更新

要明确地抑制gcc中单个函数的内联,请使用:

 void foo() __attribute__ ((noinline)) { ... } 

另请参阅如何告诉gcc不要内联函数?


除非编译-O0(禁用优化),否则会自动内联这样的函数。

在C ++中,您可以使用inline关键字提示编译器

如果编译器不接受您的提示,则可能在函数内部使用了太多的寄存器/分支。 通过将“复杂”块提取到它自己的函数中,几乎可以肯定这种情况。


更新我注意到你添加了它们是外部符号的事实。 (请用最重要的信息更新问题)。 嗯,从某种意义上说,对于外部function,所有赌注都是关闭的。 我无法真正相信gcc会根据定义将所有复杂函数内联到一个微小的调用者中,因为它只是从那里调用。 也许您可以提供一些演示行为的示例代码,我们可以找到适当的优化标志来解决这个问题?

还有 ,这是C还是C ++? 在C ++中,我知道将内部简单的决策函数包含在内是很常见的(主要是在类声明中定义的成员)。 这不会像简单(外部)C函数那样产生链接冲突。

您还可以定义模板函数,这些函数将在所有编译模块中完美地内联,而不会导致链接冲突。

我希望你使用的是C ++,因为它会给你很多选择。

我会这样做:

 static void complex_function() {} void foo() { if(simple_case) { // do whatever return; } else { complex_function(); } } 

编译器我坚持内联complex_function(),在这种情况下你可以使用noinline属性。

我可能会重构代码以鼓励内联简单案例。 也就是说,你可以使用-finline-limit来让gcc考虑内联更大的函数,或者使用-fomit-frame-pointer -fno-exceptions来最小化堆栈帧。 (注意后者可能会破坏调试并导致C ++exception行为exception。)

但是,你可能无法通过调整编译器选项获得太多,并且必须重构。

看到这些是外部调用,gcc可能会将它们视为不安全并为函数调用保留寄存器(很难知道它没有看到它保留的寄存器,包括你说’未使用’的寄存器)。 出于好奇,在禁用所有优化的情况下,这种过多的寄存器溢出是否仍会发生?