#pragma omp parallel
{
for (i=1; i<1024; i++)
#pragma omp for
for (j=1; j<1024; j++)
A[i][j] = 2*A[i-1][j];
}
このコードを実行するために 12 個のスレッドを使用しています。スピードアップするために何をしなければならないか提案はありますか?
#pragma omp parallel
{
for (i=1; i<1024; i++)
#pragma omp for
for (j=1; j<1024; j++)
A[i][j] = 2*A[i-1][j];
}
このコードを実行するために 12 個のスレッドを使用しています。スピードアップするために何をしなければならないか提案はありますか?
A の型が 64 バイトより小さいと仮定すると、この方法で内側のループを並列化しようとすると、キャッシュ ラインで誤った共有が発生する可能性が高くなります。
A が 4 バイト int のアラインされた配列であるとします。同じキャッシュ ラインに A[i][0] から A[i][15] があります。これは、12 個のスレッドすべてが必要な部分ごとに同時に行を読み取ろうとすることを意味します。これをそのままにしておくと、複数のコア間で行が共有される可能性がありますが、書き込もうとすると戻ってきます。それを変更するために、各コアが回線の所有権を取得しようとします。
CPU キャッシュは通常、MESI ベースのプロトコルに基づいており、ストアの試行により所有権の読み取りが発行され、リクエスターを除く他の各コアのラインが無効になります。12 並列 (または、6 コア * それぞれ 2 スレッドの場合は 6) を発行すると、競合が発生し、最初にラインを獲得したラインが、それを変更する前に詮索によって彼からプリエンプトされる可能性が非常に高くなります(その可能性は低いですが)。結果は非常に厄介で、回線が順番に各コアに移動し、変更され、別のコアによってスヌープされるまでに時間がかかる場合があります。これは、次の連続する 16 要素のグループごとに繰り返されます (ここでも、int を想定しています)。
あなたができることは次のとおりです。
ただし、これにより、コードの空間的局所性とストリーミング プロパティが失われるため、CPU の可能性を最大限に引き出すことができなくなります。代わりに、次のことができます。
スレッドがあまりにも多くのストリームに遭遇すると、それらを追跡できなくなる可能性があるため、ここにはまだ欠点があります。したがって、3番目のアプローチは-
1) コアはいくつありますか? それ以上の並列処理の高速化は得られません。他の人が言ったように、おそらくはるかに少ないでしょう。
2) 内部インデックスj
は 1 ではなく 0 から開始する必要があるようです。
3) その内側のループは、次のように、ポインターと展開を求めて叫びます。
double* pa = &A[i][0];
double* pa1 = &A[i-1][0];
for (j = 0; j < 1024; j += 8){
*pa++ = 2 * *pa1++;
*pa++ = 2 * *pa1++;
*pa++ = 2 * *pa1++;
*pa++ = 2 * *pa1++;
*pa++ = 2 * *pa1++;
*pa++ = 2 * *pa1++;
*pa++ = 2 * *pa1++;
*pa++ = 2 * *pa1++;
}
また...
double* pa = &A[i][0];
double* paEnd = &A[i][1024];
double* pa1 = &A[i-1][0];
for (; pa < paEnd; pa += 8, pa1 += 8){
pa[0] = 2 * pa1[0];
pa[1] = 2 * pa1[1];
pa[2] = 2 * pa1[2];
pa[3] = 2 * pa1[3];
pa[4] = 2 * pa1[4];
pa[5] = 2 * pa1[5];
pa[6] = 2 * pa1[6];
pa[7] = 2 * pa1[7];
}
どちらか速い方。