78

私は次のコードをコンパイルしようとしていました:

#pragma omp parallel shared (j)
{
   #pragma omp for schedule(dynamic)
   for(i = 0; i != j; i++)
   {
      // do something
   }
}

しかし、次のエラーが発生しました:エラー:無効な制御述語

OpenMP標準では、コンストラクターの場合、parallel for次の演算子のいずれかを「のみ」許可すると規定されています:<、、。<=> >=

許可しない理由がわかりませんi != jstatic scheduleの場合、コンパイラは各スレッドに割り当てられた反復回数を事前に計算する必要があるため、理解できました。しかし、例えばそのような場合になぜこの制限があるのか​​理解できません。手がかりはありますか?


編集:私が作ったとしても、for(i = 0; i != 100; i++)私は「<」または「<=」を置くことができたかもしれませんが。

4

5 に答える 5

79

私はこのテーマについてOpenMP開発者に電子メールを送りました。私が得た答えは次のとおりです。

signed intの場合、ラップアラウンド動作は未定義です。許可する!=と、プログラマーは予期しないトリップカウントを取得する可能性があります。問題は、コンパイラがループのトリップカウントを計算するコードを生成できるかどうかです。

次のような単純なループの場合:

for( i = 0; i < n; ++i )

コンパイラーは、n> = 0の場合は「n」回の反復があり、n<0の場合はゼロ回の反復があると判断できます。

次のようなループの場合:

for( i = 0; i != n; ++i ) 

繰り返しますが、 n> = 0の場合、コンパイラは'n'回の反復があると判断できるはずです。n <0の場合、反復回数はわかりません。

次のようなループの場合:

for( i = 0; i < n; i += 2 )

コンパイラーは、トリップカウント(ループ反復カウント)をn> = 0の場合はfloor((n + 1)/ 2)として、n<0の場合は0として計算するコードを生成できます。

次のようなループの場合:

for( i = 0; i != n; i += 2 )

コンパイラは、「i」が「n」にヒットするかどうかを判別できません。'n'が奇数の場合はどうなりますか?

次のようなループの場合:

for( i = 0; i < n; i += k )

コンパイラは、ループがカウントアップする必要があることを知っているため、n> = 0の場合はfloor((n + k-1)/ k)、n<0の場合は0としてトリップカウントを計算するコードを生成できます。この場合、k <0の場合、それは合法的なOpenMPプログラムではありません。

次のようなループの場合:

for( i = 0; i != n; i += k )

コンパイラは、私がカウントアップしているかダウンしているかさえ知りません。'i'が'n'にヒットするかどうかはわかりません。無限ループの可能性があります。

クレジット:OpenMP ARB

于 2012-11-15T18:14:14.870 に答える
19

見た目とは異なり、schedule(dynamic)動的な数の要素では機能しません。むしろ、スレッドへの反復ブロックの割り当てが動的です。静的スケジューリングでは、この割り当てはワークシェアリング構成の開始時に事前に計算されます。動的スケジューリングでは、反復ブロックは先着順でスレッドに割り当てられます。

OpenMP標準は、ワークシェア構成が検出されると反復量が事前に計算されることを明確に示しています。したがって、ループカウンターはループの本体内で変更できません(OpenMP 3.1仕様、§2.5.1-ループ構成)。

関連する各ループの反復回数は、最も外側のループに入る前に計算されます。関連するループの実行により、反復回数の計算に使用される値が変更された場合、動作は指定されていません。

折りたたまれたループの反復回数を計算するために使用される整数型(またはFortranの場合はkind)は、実装によって定義されます。

ワークシェアリングループには、0、1、...、N-1の番号が付けられた論理反復があります。ここで、Nはループ反復の数であり、論理番号は、関連するループが実行された場合に反復が実行されるシーケンスを示します。単一のスレッドによって。この schedule句は、関連するループの反復をチャンクと呼ばれる連続した空でないサブセットに分割する方法と、これらのチャンクをチームのスレッド間で分散する方法を指定します。各スレッドは、暗黙のタスクのコンテキストで割り当てられたチャンクを実行します。チャンクサイズ式は、ループ構造でプライベートにされた変数の元のリスト項目を使用して評価されます。この式の評価の副作用が発生するかどうか、どのような順序で、何回発生するかは不明です。ループ構造の節式で変数を使用すると、scheduleそれを囲むすべての構造で変数への暗黙の参照が発生します。

これらの関係演算子の制限の背後にある理論的根拠は非常に単純です。ループの方向を明確に示し、反復回数を簡単に計算でき、C /C++およびFortranのOpenMPワークシェアリングディレクティブと同様のセマンティクスを提供します。 。また、他のリレーショナル操作では、ループがどのように進行するかを理解するためにループ本体を綿密に検査する必要があります。これは多くの場合受け入れられず、実装が煩雑になります。

OpenMP 3.0では、task反復回数が不明なループの並列化を可能にする明示的な構造が導入されました。ただし、落とし穴があります。タスクはかなりのオーバーヘッドをもたらし、ループの反復ごとに1つのタスクは、これらの反復の実行にかなりの時間がかかる場合にのみ意味があります。そうしないと、オーバーヘッドが実行時間を支配することになります。

于 2012-11-09T17:19:52.727 に答える
5

答えは簡単です。OpenMPは、スレッドのチームの早期終了を許可しません。==または!=の場合、OpenMPにはループがいつ停止するかを判別する方法がありません。1. 1つ以上のスレッドが終了条件に達する可能性がありますが、これは一意ではない可能性があります。2. OpenMPには、状態を検出しない可能性のある他のスレッドをシャットダウンする方法がありません。

于 2012-11-15T17:25:14.910 に答える
2

私が声明を見たとしたら

for(i = 0; i != j; i++)

ステートメントの代わりに使用

for(i = 0; i < j; i++)

なぜプログラマーがその選択をしたのか疑問に思うでしょう。同じことを意味する可能性があることを気にしないでください。OpenMPは、コードの特定の明確さを強制するために、構文上の難しい選択を行っている可能性があります。

これは、使用の課題を提起し、!=それが許可されない理由を説明するのに役立つ可能性のあるコードです。

#include <cstdio>

int main(){
    int j=10;
   #pragma omp parallel for
   for(int i = 0; i < j; i++){
    printf("%d\n",i++);
   }
}

iステートメントとループ自体の両方でインクリメントされfor、無限ループの可能性(保証ではありません)につながることに注意してください。

述語がの場合<、コンパイラがループ内で変更をチェックし、iそれらの変更がループの境界にどのように影響するかを判断しなくても、ループの動作を並列コンテキストで明確に定義できます。

述語がである!=場合、ループの動作はもはや明確に定義されておらず、範囲が無限である可能性があり、簡単な並列細分割が妨げられます。

于 2012-11-09T18:10:03.037 に答える
0

これを実現するために既存の機能を拡張する以外に、おそらく正当な理由はないと思います。

IIRCは元々、これらを静的にする必要がありました。これにより、コンパイル時にループコードを生成する方法を決定できるようになりました。これは、そこからの二日酔いである可能性があります。

于 2012-11-09T17:19:49.017 に答える