ような配列は、ここではGCC 6と7はstd::array
を使用するときに最適化することができないいくつかのコードです:g++ -std=c++14 -O3 -march=haswell -S -DC_ARRAY
と上記のコンパイルGCCが整列STDを最適化するために失敗した:: C配列
#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];
}
}
は、素敵なコードを生成します:
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ビットレジスタを介して一度に4つの倍精度を追加する2つの展開されていない反復です。しかし、あなたは-DC_ARRAY
せずにコンパイルした場合、あなたはこの始まる巨大な混乱を取得:
mov rax, rdi
shr rax, 3
neg rax
and eax, 3
je .L7
(std::array
の代わりに、プレーンなC配列を使用して)、この場合に生成されたコードは、入力のアライメントをチェックするようですarray-- 32バイトに整列されたtypedefで指定されています。
std::array
の内容がstd::array
と同じになっているとGCCは理解していないようです。これは、C配列の代わりにstd::array
を使用するとランタイムコストが発生しないという前提を壊します。
この問題を解決する簡単な方法がありますか?これまでのところ私は醜いハックを思い付いた:
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を使用すると、問題は発生しません。 、GCCは、Cの場合と同様のstd ::配列のケースを最適化するために管理しhttps://godbolt.org/g/IXIOst
FWIW、打ち鳴らすは 'alignas'は、データメンバに、ではないのtypedefであることが必要と文句を言いますが、' Vec'を変更する場合'std :: array <...>'を整列したデータメンバとして保持し、それに 'operator []'オーバーロードを与えるネストされたクラスに渡すと、clangはこれを最適化するために管理します。 GCCは依然としてそうではありません。 – hvd
'std :: array'の基礎となる配列は、' std :: array'と同じ配列を持っていますか? –
'' Vec'がカスタム 'operator []'で 'double data [my_elements] alignas(32);'を保持するクラスとして実装されている場合、GCCはこれを最適化するために管理します。私は問題が 'array :: operator []'がその配列されていない 'array :: _ M_elems'メンバーから来るアラインメントされていない' double 'を返すということであり、アライメントされた配列の一部であるという事実は、オプティマイザがそれを見ることができるようにします。 – hvd