Las*_*sie 7 c++ x86 assembly gcc compiler-optimization
为什么gcc用零而不是仅剩余的96个整数填充整个数组?非零初始值设定项都在数组的开头。
void *sink;
void bar() {
int a[100]{1,2,3,4};
sink = a; // a escapes the function
asm("":::"memory"); // and compiler memory barrier
// forces the compiler to materialize a[] in memory instead of optimizing away
}
Run Code Online (Sandbox Code Playgroud)
MinGW8.1和gcc9.2都使asm像这样(Godbolt编译器资源管理器)。
# gcc9.2 -O3 -m32 -mno-sse
bar():
push edi # save call-preserved EDI which rep stos uses
xor eax, eax # eax=0
mov ecx, 100 # repeat-count = 100
sub esp, 400 # reserve 400 bytes on the stack
mov edi, esp # dst for rep stos
mov DWORD PTR sink, esp # sink = a
rep stosd # memset(a, 0, 400)
mov DWORD PTR [esp], 1 # then store the non-zero initializers
mov DWORD PTR [esp+4], 2 # over the zeroed part of the array
mov DWORD PTR [esp+8], 3
mov DWORD PTR [esp+12], 4
# memory barrier empty asm statement is here.
add esp, 400 # cleanup the stack
pop edi # and restore caller's EDI
ret
Run Code Online (Sandbox Code Playgroud)
(启用SSE后,它将使用movdqa加载/存储功能复制所有4个初始化程序)
为什么GCC不能像Clang那样只对最后96个元素进行lea edi, [esp+16]memset和with rep stosd? 这是错过的优化,还是以这种方式更有效?(C实际上调用memset而不是内联rep stos)
编者注:该问题最初具有未优化的编译器输出,其工作方式相同,但at处的低效率代码-O0无法证明任何事情。但事实证明,即使在时,GCC也错过了此优化-O3。
将指针传递a给非内联函数将是迫使编译器实现的另一种方式a[],但是在32位代码中会导致汇编的大量混乱。(堆栈args会导致push,而push与存储中的数据混合到堆栈中以初始化数组。)
使用volatile a[100]{1,2,3,4}获取GCC来创建然后复制该数组,这是很疯狂的。通常,volatile这有助于查看编译器如何初始化局部变量或将其布置在堆栈上。
理论上你的初始化可能是这样的:
int a[100] = {
[3] = 1,
[5] = 42,
[88] = 1,
};
Run Code Online (Sandbox Code Playgroud)
因此,从缓存和可优化性的角度来看,首先将整个内存块清零,然后设置各个值可能会更有效。
行为变化可能取决于:
当然,在您的情况下,初始化是在数组的开头压缩的,并且优化将是微不足道的。
所以看起来 gcc 在这里采用的是最通用的方法。看起来缺少优化。
| 归档时间: |
|
| 查看次数: |
159 次 |
| 最近记录: |