一番簡単なのは、4 つのスレッドに分割して、それぞれの UYVY エラーを計算することだと思います。それらを個別の値にする代わりに、それらを配列にします。
double sqError[4] = {0};
const int numBytes = width * height * 2;
#pragma omp parallel for
for( int elem = 0; elem < 4; elem++ ) {
for( int i = elem; i < numBytes; i += 4 ) {
int val = refData[i] - calData[i];
sqError[elem] += (double)(val*val);
}
}
このように、各スレッドは 1 つのものに対して排他的に動作し、競合は発生しません。
おそらく、これは OMP の最も高度な使用法ではないかもしれませんが、スピードアップが見られるはずです。
パフォーマンスの低下についてのコメントの後、いくつかの実験を行ったところ、実際にはパフォーマンスが低下していることがわかりました。これはキャッシュミスが原因であると思われます。
あなたが言った:
今回はopenMPでパフォーマンスヒット:時間:0.040637、シリアル時間:0.018670
そこで、各変数のリダクションと単一のループを使用して作り直しました。
#pragma omp parallel for reduction(+:e0) reduction(+:e1) reduction(+:e2) reduction(+:e3)
for( int i = 0; i < numBytes; i += 4 ) {
int val = refData[i] - calData[i];
e0 += (double)(val*val);
val = refData[i+1] - calData[i+1];
e1 += (double)(val*val);
val = refData[i+2] - calData[i+2];
e2 += (double)(val*val);
val = refData[i+3] - calData[i+3];
e3 += (double)(val*val);
}
4 コア マシンでのテスト ケースでは、4 倍弱の改善が見られました。
serial: 2025 ms
omp with 2 loops: 6850 ms
omp with reduction: 455 ms
[編集]コードの最初の部分が非並列バージョンよりもパフォーマンスが悪かった理由について、Hristo Iliev氏は次のように述べています。
あなたの最初のコードは、マルチスレッド コードでの偽共有のひどい例です。sqError にはそれぞれ 8 バイトの要素が 4 つしかないため、1 つのキャッシュ ラインに収まります (最新の x86 CPU では半分のキャッシュ ラインでも)。4 つのスレッドが常に隣接する要素に書き込みを行っているため、偽共有による大量のコア間キャッシュの無効化が発生します。代わりにこのような構造を使用することで、これを回避できます struct _error { double val; ダブルパッド[7]; } sqError[4]; これで、各 sqError[i].val が個別のキャッシュ ラインに配置されるため、偽の共有がなくなります。