MSVC编译器对展开次数超2的指针比较循环生成低效代码咨询
MSVC指针比较循环展开超过2次时的低效代码问题
当循环展开次数超过2次且以指针比较作为退出条件时,MSVC编译器会生成低效代码:在循环中插入冗余指令,先在循环前修改指针,再在循环内通过LEA指令恢复指针值用于比较。
示例1:x86(32位)MSVC编译(参数-O2 -arch:IA32)
C代码
void proc(unsigned *a, unsigned *stop) { do { a[0]++; a[1]++; a[2]++; a[3]++; a += 4; } while (a != stop); }
生成的汇编代码
void proc(unsigned int *,unsigned int *) PROC ; proc, COMDAT mov eax, DWORD PTR _a$[esp-4] mov edx, DWORD PTR _stop$[esp-4] add eax, 8 npad 5 $LL4@proc: inc DWORD PTR [eax-8] lea eax, DWORD PTR [eax+16] inc DWORD PTR [eax-20] lea ecx, DWORD PTR [eax-8] inc DWORD PTR [eax-16] inc DWORD PTR [eax-12] cmp ecx, edx jne SHORT $LL4@proc ret 0
问题范围与对比
该问题同样存在于AVX、SSE等其他类型循环中。使用计数器的代码在MSVC下效率更高,但其他编译器可针对此类指针比较循环生成最优代码;仅当展开次数为1或2次时,MSVC生成的代码才是最优的:
展开2次的C代码
void proc(unsigned *a, unsigned *stop) { do { a[0]++; a[1]++; a += 2; } while (a != stop); }
生成的汇编代码
_a$ = 8 ; size = 4 _stop$ = 12 ; size = 4 _proc PROC ; COMDAT mov ecx, DWORD PTR _stop$[esp-4] mov eax, DWORD PTR _a$[esp-4] $LL4@proc: inc DWORD PTR [eax] inc DWORD PTR [eax+4] add eax, 8 cmp eax, ecx jne SHORT $LL4@proc ret 0 _proc ENDP
ALU资源压力更大的示例
C代码
unsigned Sum(unsigned *a, unsigned *stop) { unsigned sum = 0; do { sum += a[0] + a[1] + a[2] + a[3]; a += 4; } while (a != stop); return sum; }
生成的汇编代码
mov ecx, DWORD PTR _a$[esp-4] xor eax, eax push esi mov esi, DWORD PTR _stop$[esp] add ecx, 8 npad 2 $LL4@Sum: mov edx, DWORD PTR [ecx-8] lea ecx, DWORD PTR [ecx+16] add edx, DWORD PTR [ecx-20] add edx, DWORD PTR [ecx-12] add edx, DWORD PTR [ecx-16] add eax, edx lea edx, DWORD PTR [ecx-8] cmp edx, esi jne SHORT $LL4@Sum pop esi ret 0
临时解决方法
在单次循环迭代中多次更新指针,此时MSVC会生成无冗余LEA指令的精简代码,性能表现良好。建议最优指针步长为2,因为ARM64平台下MSVC在pointer_step==2时会使用ldp/stp指令,而步长为1时不会:
修改后的C代码
void proc(unsigned *a, unsigned *stop) { do { a[0]++; a[1]++; a += 2; a[0]++; a[1]++; a += 2; } while (a != stop); }
生成的汇编代码
; proc, COMDAT mov ecx, DWORD PTR _stop$[esp-4] mov eax, DWORD PTR _a$[esp-4] $LL4@proc: inc DWORD PTR [eax] inc DWORD PTR [eax+4] inc DWORD PTR [eax+8] inc DWORD PTR [eax+12] add eax, 16 ; 00000010H cmp eax, ecx jne SHORT $LL4@proc ret 0
问题咨询
该编译器异常行为的原因是什么?是否有修复方案?
内容的提问来源于Stack Exchange,提问作者Igor Pavlov
相关产品推荐
相关产品推荐

