这个问题部分是GCC 5.1 Loop unrolling的后续问题。
根据GCC documentation,并且如我对上述问题的回答所述,-funroll-loops
等标志打开"完全循环剥离(即完全删除循环一个小的恒定迭代次数)" 。因此,当启用这样的标志时,如果确定这将优化给定代码段的执行,则编译器可以选择展开循环。
尽管如此,我注意到在我的一个项目中,GCC有时会展开循环,即使相关标志未启用。例如,请考虑以下简单的代码:
int main(int argc, char **argv)
{
int k = 0;
for( k = 0; k < 5; ++k )
{
volatile int temp = k;
}
}
使用-O1
进行编译时,循环展开,并使用任何现代版本的GCC生成以下汇编代码:
main:
movl $0, -4(%rsp)
movl $1, -4(%rsp)
movl $2, -4(%rsp)
movl $3, -4(%rsp)
movl $4, -4(%rsp)
movl $0, %eax
ret
即使使用额外的-fno-unroll-loops -fno-peel-loops
进行编译以确保标志禁用,GCC仍会意外地执行上述示例的循环展开。
这一观察引出了以下密切相关的问题。为什么GCC执行循环展开,即使禁用了与此行为相对应的标志?展开也是由其他标志控制的,这些标志可以使编译器在某些情况下展开循环,即使-funroll-loops
被禁用了吗?有没有办法在GCC中完全禁用循环展开(使用-O0
进行编译的一部分)?
有趣的是, Clang 编译器在此处具有预期的行为,并且似乎仅在启用-funroll-loops
时执行展开,而不是在其他情况下执行。
在此先感谢您对此事项的任何其他见解将不胜感激!
答案 0 :(得分:9)
为什么GCC即使执行标记也会执行循环展开 对应这种行为是禁用的吗?
从实用的角度来看:将这样的标志传递给编译器时你想要什么?没有C ++开发人员会要求GCC展开或不展开循环,只是为了在汇编代码中有循环,有一个目标。例如,-fno-unroll-loops
的目标是牺牲一点速度以减小二进制文件的大小,如果您正在开发具有有限存储的嵌入式软件。另一方面,-funrool-loops
的目标是告诉编译器你不关心二进制文件的大小,所以它应该毫不犹豫地展开循环。
但这并不意味着编译器会盲目展开或不展开你的所有循环!
在你的例子中,原因很简单:循环只包含一个指令 - 在任何平台上都有几个字节 - 并且编译器知道这是可以忽略的,并且无论如何都将采用与循环所需的汇编代码(sub
+ mov
+ jne
x86-64)。
这就是为什么gcc 6.2,-O3 -fno-unroll-loops
改变了这段代码:
int mul(int k, int j)
{
for (int i = 0; i < 5; ++i)
volatile int k = j;
return k;
}
...到以下汇编代码:
mul(int, int):
mov DWORD PTR [rsp-0x4],esi
mov eax,edi
mov DWORD PTR [rsp-0x4],esi
mov DWORD PTR [rsp-0x4],esi
mov DWORD PTR [rsp-0x4],esi
mov DWORD PTR [rsp-0x4],esi
ret
它不会听你的,因为它(几乎取决于架构)不会改变二进制文件的大小,但速度更快。但是,如果你增加一点你的循环计数器......
int mul(int k, int j)
{
for (int i = 0; i < 20; ++i)
volatile int k = j;
return k;
}
......它遵循你的提示:
mul(int, int):
mov eax,edi
mov edx,0x14
nop WORD PTR [rax+rax*1+0x0]
sub edx,0x1
mov DWORD PTR [rsp-0x4],esi
jne 400520 <mul(int, int)+0x10>
repz ret
如果将循环计数器保持在5
但是在循环中添加了一些代码,则会得到相同的行为。
总而言之,将所有这些优化标志视为编译器的提示,并从实用的开发人员的角度出发。它始终是一种权衡,当您构建软件时,您从不想要所有或没有循环展开。
作为最后一点,另一个非常相似的例子是-f(no-)inline-functions
标志。我每天都在为编译器内联(或不是!)我的一些函数(使用inline
关键字和__attribute__ ((noinline))
使用GCC)进行内联,当我检查汇编代码时,我看到这个smartass当我想内联一个绝对太长的功能时,它仍然有时会做它想要的东西。大多数时候,这是正确的事情,我很高兴!