10

私はOpenMPを研究していて、次の例に出くわしました。

#pragma omp parallel shared(n,a,b,c,d,sum) private(i)
{
    #pragma omp for nowait
    for (i=0; i<n; i++)
        a[i] += b[i];

    #pragma omp for nowait
    for (i=0; i<n; i++)
        c[i] += d[i];
    #pragma omp barrier

    #pragma omp for nowait reduction(+:sum)
    for (i=0; i<n; i++)
        sum += a[i] + c[i];
} /*-- End of parallel region --*/

最後のforループには、nowait節とreduction節があります。これは正しいです?削減句を同期する必要はありませんか?

4

3 に答える 3

19

nowait2番目と最後のループのsはやや冗長です。OpenMP仕様nowaitでは、リージョンが終了する前に言及されているため、おそらくこれを維持できます。

しかし、nowait2番目のループの前とそれの後の明示的なバリアは互いに打ち消し合います。

最後に、sharedandprivate句について。コードでsharedは、効果はなく、まったく使用しないでください。スレッドプライベート変数が必要な場合は、並列領域privateで宣言するだけです。特に、ループ変数をループの前ではなく、ループ内で宣言する必要があります。

便利にするsharedには、OpenMPにデフォルトでは何も共有しないように指示する必要があります。誤って変数を共有することによるバグを回避するために、これを行う必要があります。これは、を指定することによって行われdefault(none)ます。これにより、次のことが可能になります。

#pragma omp parallel default(none) shared(n, a, b, c, d, sum)
{
    #pragma omp for nowait
    for (int i = 0; i < n; ++i)
        a[i] += b[i];

    #pragma omp for
    for (int i = 0; i < n; ++i)
        c[i] += d[i];

    #pragma omp for nowait reduction(+:sum)
    for (int i = 0; i < n; ++i)
        sum += a[i] + c[i];
} // End of parallel region
于 2011-06-11T13:35:25.723 に答える
9

いくつかの点で、これは宿題の問題のように思えますが、私はそれを人々のためにしたくありません。一方で、上記の答えは完全に正確ではなく、修正する必要があると思います。

まず、この例では、共有句とプライベート句の両方は必要ありませんが、使用すべきではないというコンラッドには同意しません。コードを並列化する人々の最も一般的な問題の1つは、変数がどのように使用されているかを理解するのに時間がかからないことです。共有変数を民営化および/または保護しないことは、私が目にする問題の最大数を説明します。変数がどのように使用されているかを調べ、それらを適切な共有、プライベートなどの句に入れるという演習を行うことで、問題の数を大幅に減らすことができます。

バリアに関する質問については、2番目のループで計算された値(a)が使用されないため、最初のループにnowait句を含めることができます。2番目のループは、値が計算される前に計算された値(c)が使用されない場合(つまり、依存関係がない場合)にのみnowait句を持つことができます。元のサンプルコードでは、2番目のループには待機がありませんが、3番目のループの前に明示的なバリアがあります。教授が明示的なバリアの使用を示しようとしていたので、これは問題ありません-2番目のループでnowaitを省略すると、明示的なバリアが冗長になります(ループの最後に暗黙的なバリアがあるため)。

一方、2番目のループでの待機と明示的なバリアまったく必要ない場合があります。OpenMP V3.0仕様の前は、多くの人が、仕様で明確にされていない何かが真実であると想定していました。OpenMP V3.0仕様では、セクション2.5.1ループ構成、表2-1スケジュール句の種類の値、静的(スケジュール)に以下が追加されました。

静的スケジュールの準拠実装では、次の条件が満たされる場合、スレッドへの論理反復数の同じ割り当てが2つのループ領域で使用されるようにする必要があります:1)両方のループ領域のループ反復数が同じである2)両方のループ領域同じchunk_sizeの値が指定されているか、両方のループ領域にchunk_sizeが指定されていない、および3)両方のループ領域が同じ並列領域にバインドされている。このような2つのループでの同じ論理反復間のデータ依存性が満たされることが保証され、nowait句を安全に使用できます(例については、170ページのセクションA.9を参照してください)。

この例では、どのループにもスケジュールが表示されていないため、これが成立する場合と成立しない場合があります。その理由は、デフォルトのスケジュールは実装によって定義されており、ほとんどの実装は現在デフォルトのスケジュールを静的に定義していますが、それを保証するものではないためです。教授が3つのループすべてにチャンクサイズのないスケジュールタイプの静的を設定した場合、1番目と2番目のループでnowaitを使用でき、2番目と3番目のループの間に(暗黙的または明示的な)バリアは必要ありません。まったく。

次に、3番目のループと、待機と削減についての質問に進みます。Michyが指摘したように、OpenMP仕様では、両方(reductionとnowait)を指定できます。ただし、削減を完了するために同期が必要ないというのは事実ではありません。この例では、(3番目のループの終わりにある)暗黙のバリアをnowaitで削除できます。これは、並列領域の暗黙のバリアに遭遇する前に、削減(合計)が使用されていないためです。

OpenMP V3.0仕様のセクション2.9.3.6削減条項を見ると、次のことがわかります。

nowaitが使用されていない場合、削減計算は構成の最後で完了します。ただし、nowaitも適用される構成でreduction句を使用すると、元のリストアイテムにアクセスすると競合が発生するため、すべてのスレッドがすべての反復を実行した後に同期が確実に発生しない限り、不特定の影響があります。またはセクションが構成され、削減計算が完了して、そのリスト項目の計算値が格納されます。これは、バリア同期によって最も簡単に保証できます。

つまり、3番目のループの後に並列領域で合計変数を使用する場合は、使用する前にバリア(暗黙的または明示的)が必要になります。例が今立っているように、それは正しいです。

于 2011-06-12T18:45:23.270 に答える
2

OpenMPの専門分野は次のように述べています。

ループ構造の構文は次のとおりです。

#pragma omp for [clause[[,] clause] ... ] new-line
    for-loops

where句は次のいずれかです。

 ...
 reduction(operator: list)
 ...
 nowait

したがって、より多くの句が存在する可能性があるため、reductionとnowaitステートメントの両方が存在する可能性があります。

reduction句で明示的な同期を行う必要はありません。sum変数への追加はreduction(+: sum)、以前のバリアフォースaのために同期され、ループb時に最終値があります。つまり、スレッドがループ内の作業を終了した場合、他のすべてのスレッドが同じループを終了するまで待つ必要はありませんreductionnowait

于 2011-06-11T13:24:57.810 に答える