Intel の Software Developer Manual (14.9 節) によると、AVX はメモリ アクセスのアライメント要件を緩和しました。データが処理命令に直接ロードされた場合。
vaddps ymm0,ymm0,YMMWORD PTR [rax]
ロード アドレスを揃える必要はありません。ただし、次のような専用のアライメントされたロード命令が使用されている場合
vmovaps ymm0,YMMWORD PTR [rax]
ロードアドレスは (32 の倍数に) アラインされている必要があります。アラインされていない場合、例外が発生します。
私を混乱させているのは、組み込み関数からの自動コード生成です。私の場合は gcc/g++ (4.6.3、Linux) によるものです。次のテストコードを見てください。
#include <x86intrin.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define SIZE (1L << 26)
#define OFFSET 1
int main() {
float *data;
assert(!posix_memalign((void**)&data, 32, SIZE*sizeof(float)));
for (unsigned i = 0; i < SIZE; i++) data[i] = drand48();
float res[8] __attribute__ ((aligned(32)));
__m256 sum = _mm256_setzero_ps(), elem;
for (float *d = data + OFFSET; d < data + SIZE - 8; d += 8) {
elem = _mm256_load_ps(d);
// sum = _mm256_add_ps(elem, elem);
sum = _mm256_add_ps(sum, elem);
}
_mm256_store_ps(res, sum);
for (int i = 0; i < 8; i++) printf("%g ", res[i]); printf("\n");
return 0;
}
(はい、アラインされていないアドレスに対してアラインされたロードを使用しているため、コードに問題があることはわかっていますが、ご容赦ください...)
コードをコンパイルします
g++ -Wall -O3 -march=native -o memtest memtest.C
AVXを搭載したCPU上。を使用してg ++によって生成されたコードを確認すると
objdump -S -M intel-mnemonic memtest | more
コンパイラはアラインされたロード命令を生成せず、ベクトル加算命令でデータを直接ロードすることがわかります。
vaddps ymm0,ymm0,YMMWORD PTR [rax]
メモリ アドレスがアラインされていない (OFFSET が 1) 場合でも、コードは問題なく実行されます。vaddps はアラインされていないアドレスを許容するため、これは明らかです。
2 番目の加算組み込み関数の行のコメントを外すと、vaddps はメモリ ソース オペランドを 1 つしか持てないため、コンパイラはロードと加算を融合できず、次のように生成します。
vmovaps ymm0,YMMWORD PTR [rax]
vaddps ymm1,ymm0,ymm0
vaddps ymm0,ymm1,ymm0
そして、専用のアラインされたロード命令が使用されているため、プログラム seg-fault が発生しますが、メモリ アドレスはアラインされていません。(ちなみに、_mm256_loadu_ps を使用するか、OFFSET を 0 に設定すると、プログラムはセグメンテーション フォールトしません。)
私の謙虚な意見では、これにより、プログラマーはコンパイラーに翻弄され、動作が部分的に予測不能になります。
私の質問は次のとおりです。C コンパイラに、処理命令 (vaddps など) で直接ロードを生成させるか、専用のロード命令 (vmovaps など) を生成させる方法はありますか?