我最近遇到了很多函数,其中gcc在x86上生成了非常糟糕的代码。它们都符合以下模式:
if (some_condition) {
/* do something really simple and return */
} else {
/* something complex that needs lots of registers */
}
将简单的情况看作是一个非常小的东西,一半或更多的工作花在推动和弹出不会被修改的寄存器上。如果我手动编写asm,我会在复杂的情况下保存并恢复已保存的跨越调用寄存器,并且在简单的情况下完全避免触及堆栈指针。
有没有什么方法可以让gcc变得更聪明并自己动手?最好使用命令行选项,而不是源代码中的丑陋黑客...
编辑:为了使其具体,这里有一些非常接近我正在处理的一些功能:
if (buf->pos < buf->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:我应该首先提到:这些函数无法内联。他们有外部链接,他们是图书馆代码。允许在应用程序中内联它们会导致各种问题。
答案 0 :(得分:2)
<强>更新强>
要明确地抑制gcc中单个函数的内联,请使用:
void foo() __attribute__ ((noinline))
{
...
}
另见How can I tell gcc not to inline a function?
除非编译-O0(禁用优化),否则将自动内联这样的函数。
在C ++中,您可以使用内联关键字
提示编译器如果编译器不接受您的提示,则可能在函数内部使用了太多的寄存器/分支。通过将“复杂”块提取到它自己的函数中,几乎可以肯定这种情况。
更新我注意到你添加了一个事实,即它们是外部符号。 (请用最重要的信息更新问题)。嗯,从某种意义上说,对于外部功能,所有的赌注都是关闭的。我真的不相信gcc会根据定义将所有复杂的函数内联到一个微小的调用者简单,因为它只是从那里调用。也许您可以提供一些演示行为的示例代码,我们可以找到适当的优化标志来解决这个问题吗?
,这是C还是C ++?在C ++中,我知道将内部简单的决策函数包含在内是很常见的(主要是在类声明中定义的成员)。这不会像简单(外部)C函数那样产生链接冲突。
此外,您可以定义模板函数,这些函数将在所有编译模块中完美地内联,而不会导致链接冲突。
我希望您使用的是C ++,因为它会为您提供大量选项。
答案 1 :(得分:2)
我会这样做:
static void complex_function() {}
void foo()
{
if(simple_case) {
// do whatever
return;
} else {
complex_function();
}
}
编译器我坚持内联complex_function(),在这种情况下你可以使用noinline属性。
答案 2 :(得分:1)
也许升级你的gcc版本? 4.6刚刚发布。据我了解,它有“部分内联”的可能性。也就是说,函数的易于集成的外部部分被内联并且昂贵的部分被转换成调用。但我必须承认,我自己并没有尝试过。
编辑:我在ChangeLog中引用的声明:
现在支持部分内联 默认情况下启用-O2和更高版本。 该功能可以通过控制 -fpartial-内联。
部分内联拆分功能 短暂的热门回归。这允许 更积极的内联热 路径领先以提高绩效 经常减少代码大小(因为 寒冷的部分功能不是 重复)。
...
优化大小时的内联 (无论是在一个程序的寒冷地区 或者用-Os编译时是 改进以更好地处理C ++程序 具有更大的抽象惩罚, 导致更小更快的代码。
答案 3 :(得分:0)
我可能会重构代码以鼓励内联简单案例。也就是说,您可以使用-finline-limit
使gcc
考虑内联更大的函数,或使用-fomit-frame-pointer -fno-exceptions
来最小化堆栈帧。 (注意后者可能会破坏调试并导致C ++异常行为不当。)
但是,你可能无法从调整编译器选项中获得太多,并且必须重构。
答案 4 :(得分:0)
看到这些是外部调用,gcc可能会将它们视为不安全并保留函数调用的寄存器(很难知道它没有看到它保留的寄存器,包括那些你说'不使用的寄存器' “)。出于好奇,在禁用所有优化的情况下,这种过多的寄存器溢出是否仍会发生?