这是一段使用std::array
时,GCC 6和7无法进行优化的代码:
#include <array>
static constexpr size_t my_elements = 8;
class Foo
{
public:
#ifdef C_ARRAY
typedef double Vec[my_elements] alignas(32);
#else
typedef std::array<double, my_elements> Vec alignas(32);
#endif
void fun1(const Vec&);
Vec v1{{}};
};
void Foo::fun1(const Vec& __restrict__ v2)
{
for (unsigned i = 0; i < my_elements; ++i)
{
v1[i] += v2[i];
}
}
使用g++ -std=c++14 -O3 -march=haswell -S -DC_ARRAY
编译以上内容会生成优美的代码:
vmovapd ymm0, YMMWORD PTR [rdi]
vaddpd ymm0, ymm0, YMMWORD PTR [rsi]
vmovapd YMMWORD PTR [rdi], ymm0
vmovapd ymm0, YMMWORD PTR [rdi+32]
vaddpd ymm0, ymm0, YMMWORD PTR [rsi+32]
vmovapd YMMWORD PTR [rdi+32], ymm0
vzeroupper
基本上这是通过256位寄存器每次添加四个双精度浮点数展开的两轮迭代。但如果你没有使用-DC_ARRAY
编译,你将得到一个从以下内容开始的巨大混乱:
mov rax, rdi
shr rax, 3
neg rax
and eax, 3
je .L7
在这种情况下生成的代码(使用std::array
而不是普通的C数组)似乎会检查输入数组的对齐方式,尽管在typedef中指定为对齐到32字节。似乎GCC不理解
std::array
的内容与std::array
本身相同。这打破了使用std::array
而不是C数组不会产生运行时成本的假设。有什么简单的东西我错过了可以解决这个问题吗?到目前为止,我想出了一个丑陋的hack:
void Foo::fun2(const Vec& __restrict__ v2)
{
typedef double V2 alignas(Foo::Vec);
const V2* v2a = static_cast<const V2*>(&v2[0]);
for (unsigned i = 0; i < my_elements; ++i)
{
v1[i] += v2a[i];
}
}
还要注意:如果my_elements
的值为4而不是8,则问题不会发生。 如果使用Clang编译器,则问题也不会发生。
您可以在此处实时查看:https://godbolt.org/g/IXIOst
alignas
必须放在数据成员上而不是typedef上,但如果将Vec
更改为一个嵌套类,其中包含一个作为对齐数据成员的std::array<...>
,并给它加上operator[]
重载,那么clang确实能够进行优化。GCC仍不能。 - user743382std::array
底层数组的对齐方式和std::array
本身的对齐方式相同吗? - user2486888