GCC中的循环展开行为
Loop unrolling behaviour in GCC
这个问题在一定程度上是GCC 5.1循环展开的后续问题。
根据GCC文档,以及我对上述问题的回答中所述,诸如-funroll-loops
之类的标志打开"完全循环剥离(即通过少量常数次迭代完全去除循环)"。因此,当启用这样的标志时,如果编译器确定展开循环将优化给定代码段的执行,则可以选择展开循环。
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
编译的一部分)?
-funroll-loops
时才执行展开,而在其他情况下则不执行。
提前感谢,任何关于此事的额外见解将不胜感激!
为什么GCC执行循环展开,即使标志对应的这个行为被禁用了吗?
从实用的角度考虑:当你把这样的标志传递给编译器时,你想要什么?没有c++开发人员会要求GCC展开或不展开循环,只是为了在汇编代码中有循环或没有循环,这是有目的的。例如,如果您正在开发存储有限的嵌入式软件,那么-fno-unroll-loops
的目标是牺牲一点速度来减少二进制文件的大小。另一方面,-funrool-loops
的目的是告诉编译器,您并不关心二进制文件的大小,因此它应该毫不犹豫地展开循环。
在你的例子中,原因很简单:循环只包含一条指令——在任何平台上都是几个字节——编译器知道这是可以忽略的,并且无论如何都会占用与循环所需的汇编代码(x86-64上的sub
+ mov
+ jne
)几乎相同的大小。
这就是为什么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
关键字和GCC的__attribute__ ((noinline))
)作斗争,当我检查汇编代码时,我看到这个聪明的家伙有时仍然在做它想做的事情,当我想内联一个函数时,它肯定太长了。大多数时候,这是正确的事情,我很高兴!
- 循环展开 - G++ 与 Clang++
- 通过循环展开和阻塞进行优化
- 为什么Visual Studio Compiler不在我的Mersenne-Twister实现中循环展开?
- 循环展开和元编程(TMP)
- 为什么在 XCode 中默认循环展开
- Clang或GCC能够自动化手动展开的循环
- 如何使用某些参数展开某个循环,例如 GCC 中的最大展开时间
- 是否有任何预处理器指令控制循环展开
- C++模板元编程成员函数循环展开
- C++循环展开性能差异(欧拉项目)
- SSE 内联函数和循环展开
- 对于循环在 gcc -O3 使用 OpenMP 优化后不会加速
- 仅当循环展开时,才没有未指定的启动错误
- C++ 循环展开性能
- 嵌套循环展开
- 有利于循环展开的条件和返回边际减小的点
- c++循环展开,边界
- GCC中的循环展开行为
- c++循环展开为编译时常数小值
- VS2010 c++优化做循环展开吗?