我有一个POD结构体的数组,并尝试对其中一个字段进行求和。以下是最小化的示例:
struct Item
{
int x = 0;
int y = 0;
};
typedef Item Items[2];
struct ItemArray
{
Items items;
int sum_x1() const;
int sum_x2() const;
};
int ItemArray::sum_x1() const
{
int total = 0;
for (unsigned ii = 0; ii < 2; ++ii)
{
total += items[ii].x;
}
return total;
}
int ItemArray::sum_x2() const
{
int total = 0;
for (const Item& item : items)
{
total += item.x;
}
return total;
}
这两个求和函数执行的是同一个操作,Clang编译它们时会生成相同的代码。但是在x86_64架构上,使用GCC 6编译且开启-O3
优化选项时,它们并不相同。这是一个看起来良好的sum_x1()
函数:
这两个求和函数执行的是同一个操作,Clang编译它们时会生成相同的代码。但是在x86_64架构上,使用GCC 6编译且开启-O3
优化选项时,它们并不相同。这是一个看起来良好的sum_x1()
函数:
mov eax, DWORD PTR [rdi+8]
add eax, DWORD PTR [rdi]
ret
现在看看sum_x2()
函数:
lea rdx, [rdi+16]
lea rcx, [rdi+8]
xor eax, eax
add eax, DWORD PTR [rdi]
cmp rdx, rcx
je .L12
lea rcx, [rdi+16]
add eax, DWORD PTR [rdi+8]
cmp rdx, rcx
je .L2
lea rcx, [rdi+24]
add eax, DWORD PTR [rdi+16]
cmp rdx, rcx
je .L2
lea rcx, [rdi+32]
add eax, DWORD PTR [rdi+24]
cmp rdx, rcx
je .L2
lea rcx, [rdi+40]
add eax, DWORD PTR [rdi+32]
cmp rdx, rcx
je .L2
lea rcx, [rdi+48]
add eax, DWORD PTR [rdi+40]
cmp rdx, rcx
je .L2
lea rcx, [rdi+56]
add eax, DWORD PTR [rdi+48]
cmp rdx, rcx
je .L2
lea rcx, [rdi+64]
add eax, DWORD PTR [rdi+56]
cmp rdx, rcx
je .L2
lea rcx, [rdi+72]
add eax, DWORD PTR [rdi+64]
cmp rdx, rcx
je .L2
add eax, DWORD PTR [rdi+72]
ret
.L2:
rep ret
.L12:
rep ret
为什么在循环长度固定为2时,GCC会发出一个长度可变的展开循环,最多可以展开10次?它只在成员函数中这样做--将sum_x2
更改为自由函数可以修复它。ICC也非常奇怪地优化了
sum_x2()
,尽管生成的代码完全不同。与GCC不同,无论sum_x2()
是成员函数还是自由函数都没有关系--两者都不好。我正在使用GCC 6,但所有版本的GCC似乎都存在这个问题。添加
-march=haswell
会使它变得更糟,增加数组大小为2的元素的迭代次数达到15。 GCC 5和7生成更复杂的代码,添加了SIMD指令。
我想确定此问题的确切原因,以便我可以找到并修复类似的情况。了解在GCC 6中触发此行为的原因将非常有帮助。 我的代码中有很多基于范围的for循环,如果GCC不能生成合理的代码,我将别无选择。尝试一下:https://godbolt.org/g/9GK4jy 更多相关的疯狂:https://godbolt.org/g/BGYggD(最优代码为3条指令;GCC 6生成8条指令;GCC 7生成130条指令)
gcc
中的一个错误。您可以在此处https://gcc.gnu.org/bugzilla/报告它,并标记为未优化。 - fghj~FixedArray() noexcept = default;
,那么两个gcc版本都会生成3条指令。 - Praetoriannoexcept
、内联定义,并且什么也不做)不应该改变实施构造所需的代码。但事实上确实改变了。 - John Zwinck