Здесь некоторый код, который GCC 6 и 7 не может оптимизировать при использовании std::array
:
#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 не требует затрат времени выполнения.
Есть ли что-то простое, что мне не хватает, что бы это исправить? До сих пор я придумал уродливый взлом:
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