2つの配列を合計し、3番目の配列を出力しています(縮小ではありません)。このような:
void add_scalar(float* result, const float* a, const float* b, const int N) {
for(int i = 0; i<N; i++) {
result[i] = a[i] + b[i];
}
}
最大のスループットでこれを実行したいと思います。SSEと4つのコアを使用すると、単純に16倍の速度向上が期待できます(SSEの場合は4つ、4つのコアの場合は4つ)。SSE(およびAVX)を使用してコードを実装しました。Visual Studio 2012には自動ベクトル化がありますが、「ループを展開する」ことでより良い結果が得られます。L1、L2、L3キャッシュ、およびメインメモリに対応する32KB未満、256KB未満、8MB未満、および8 MBを超えるコアの4つのサイズの配列(32バイトアライメント)のコードを実行します。L1の場合、展開されたSSEコード(AVXでは5〜6)を使用すると約4倍のスピードアップが見られます。それは私が期待する限りです。その後、キャッシュレベルごとに効率が低下します。次に、OpenMPを使用して各コアで実行します。配列のメインループの前に「#pragmaompparallelfor」を配置しました。ただし、私が得る最高のスピードアップは、SSE + OpenMPで5〜6倍です。16倍のスピードアップが見られない理由を誰かが知っていますか?システムメモリからキャッシュへのアレイの「アップロード」時間が原因である可能性がありますか?コードのプロファイルを作成する必要があることはわかっていますが、それ自体が別の冒険であり、学ぶ必要があります。
#define ROUND_DOWN(x, s) ((x) & ~((s)-1))
void add_vector(float* result, const float* a, const float* b, const int N) {
__m128 a4;
__m128 b4;
__m128 sum;
int i = 0;
for(; i < ROUND_DOWN(N, 8); i+=8) {
a4 = _mm_load_ps(a + i);
b4 = _mm_load_ps(b + i);
sum = _mm_add_ps(a4, b4);
_mm_store_ps(result + i, sum);
a4 = _mm_load_ps(a + i + 4);
b4 = _mm_load_ps(b + i + 4);
sum = _mm_add_ps(a4, b4);
_mm_store_ps(result + i + 4, sum);
}
for(; i < N; i++) {
result[i] = a[i] + b[i];
}
return 0;
}
次のような競合状態の私の間違ったメインループ:
float *a = (float*)_aligned_malloc(N*sizeof(float), 32);
float *b = (float*)_aligned_malloc(N*sizeof(float), 32);
float *c = (float*)_aligned_malloc(N*sizeof(float), 32);
#pragma omp parallel for
for(int n=0; n<M; n++) { //M is an integer of the number of times to run over the array
add_vector(c, a, b, N);
}
Grizzlyの提案に基づいて修正したメインループ:
for(int i=0; i<4; i++) {
results[i] = (float*)_aligned_malloc(N*sizeof(float), 32);
}
#pragma omp parallel for num_threads(4)
for(int t=0; t<4; t++) {
for(int n=0; n<M/4; n++) { //M is an integer of the number of times to run over the array
add_vector(results[t], a, b, N);
}
}