为何func_a中的两个循环未发生C++循环融合?
为何GCC未对func_a的两个循环进行融合优化?
等价函数func_a与func_b
int func_a(int const* const arr) { int sum1 = 0; for (int i = 0; i < 200; ++i) sum1 += arr[i]; int sum2 = 0; for (int i = 0; i < 200; ++i) sum2 += arr[i]; return sum1 + sum2; } int func_b(int const* const arr) { int sum1 = 0; int sum2 = 0; for (int i = 0; i < 200; ++i) { sum1 += arr[i]; sum2 += arr[i]; } return sum1 + sum2; }
编译生成的汇编代码
以下是在g++ 13.2、std=c++20、-Ofast编译选项下生成的汇编:
func_a(int const*): lea rdx, [rdi+800] mov rax, rdi pxor xmm0, xmm0 .L2: movdqu xmm3, XMMWORD PTR [rax] add rax, 16 paddd xmm0, xmm3 cmp rax, rdx jne .L2 movdqa xmm1, xmm0 psrldq xmm1, 8 paddd xmm0, xmm1 movdqa xmm1, xmm0 psrldq xmm1, 4 paddd xmm0, xmm1 movdqa xmm1, xmm0 pxor xmm0, xmm0 .L3: movdqu xmm4, XMMWORD PTR [rdi] add rdi, 16 paddd xmm0, xmm4 cmp rdi, rdx jne .L3 movdqa xmm2, xmm0 psrldq xmm2, 8 paddd xmm0, xmm2 movdqa xmm2, xmm0 psrldq xmm2, 4 paddd xmm0, xmm2 paddd xmm0, xmm1 movd eax, xmm0 ret func_b(int const*): lea rax, [rdi+800] pxor xmm0, xmm0 .L8: movdqu xmm2, XMMWORD PTR [rdi] add rdi, 16 paddd xmm0, xmm2 cmp rax, rdi jne .L8 movdqa xmm1, xmm0 psrldq xmm1, 8 paddd xmm0, xmm1 movdqa xmm1, xmm0 psrldq xmm1, 4 paddd xmm0, xmm1 movd eax, xmm0 add eax, eax ret
问题
为何func_a中的两个循环未发生循环融合?手动融合后的func_b是我期望的func_a优化版本。
回答
GCC的循环融合优化并非在所有等价场景下都会触发,func_a未被融合主要有几方面原因:
- 数据流分析的局限性:func_a的两个循环是完全独立的,sum1和sum2分别在不同循环中累加,优化器的数据流分析没有将这两个变量的操作关联起来,没有识别到可以把它们的合并到同一个循环中执行。虽然最终数学结果等价,但优化器没有判定这两个循环属于可融合的高优先级场景。
- -Ofast的优化侧重点:
-Ofast虽属于激进优化选项,但它更偏向于放宽标准约束(如忽略NaN/Inf检查)、加速浮点运算等方向,对循环融合这类需要深度代码结构分析的优化,并没有强制触发所有可能的变换。 - 循环融合的触发规则:GCC的循环融合通常更倾向于处理有内存访问复用、操作重叠的场景,而func_a的两个循环只是重复执行相同的读取和累加,优化器选择了分别对每个循环做SIMD向量化,而非先融合再优化。
从汇编也能明显看出差异:func_b只遍历数组一次,最后将累加结果翻倍;而func_a是两次完整遍历数组,即便用了SIMD优化,重复的内存访问开销依然高于前者。
内容的提问来源于stack exchange,提问作者user5406764
相关产品推荐
相关产品推荐

