web-dev-qa-db-ja.com

!=演算子がOpenMPで許可されていないのはなぜですか?

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

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

このエラーが発生します:エラー:無効な制御述語

OpenMP リファレンスガイド を確認すると、並列の場合、「のみ」は次の演算子のいずれかを許可すると書かれています:<< = >> =。

_i != j_を許可しない理由がわかりません。 openMPは各スレッドに割り当てられた反復回数を事前に計算する必要があるため、それが静的スケジュールであるかどうかを理解できました。しかし、例えばそのような場合になぜこの制限があるのか​​理解できません。手がかりはありますか?


編集:for(i = 0; i != 100; i++)を作成しても、「<」または「<= "」を入力できます。

70
dreamcrash

私はこの主題についてOpenMP開発者に電子メールを送りました、私が得た答え:

符号付き整数の場合、ラップアラウンド動作は定義されていません。許可した場合!=、プログラマーは予期しないトリップカウントを取得する可能性があります。問題は、コンパイラがループのトリップカウントを計算するコードを生成できるかどうかです。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

クレジット:OpenMP ARB

62
dreamcrash

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

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

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

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

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

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

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

17
Hristo Iliev

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

4
bobcgausa

私が声明を見たとしたら

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への変更をチェックし、それらの変更がループにどのように影響するかを判断しなくても、ループの動作を並列コンテキストで明確に定義できます。境界。

述語が!=の場合、ループの動作は明確に定義されておらず、範囲が無限である可能性があるため、簡単に並列に分割できません。

2
Richard

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

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

0
jheriko