#pragma ompparallelと#pragmaompparallelの違い

Dec 11 2020

私は初めてでOpenMP、OpenMPを使用して2つの配列を追加するプログラムを実行しようとしています。OpenMPチュートリアルでは、forループでOpenMPを使用しているときに、#pragma omp parallelforを使用する必要があることを学びました。しかし、#pragma omp parallelでも同じことを試しましたが、正しい出力も得られます。以下は、私が伝えようとしていることのコードスニペットです。

#pragma omp parallel for
{
      for(int i=0;i<n;i++)
       {  
            c[i]=a[i]+b[i];
       }
}

そして

 #pragma omp parallel
{
      for(int i=0;i<n;i++)
       {  
            c[i]=a[i]+b[i];
       }
}

これら2つの違いは何ですか?

回答

5 dreamcrash Dec 11 2020 at 15:14

ザ・

#pragma omp parallel

parallel regionチームでを作成しますthreads。各スレッドは、parallel region囲んでいるコードのブロック全体を実行します。

OpenMP 5.1から、より正式な説明を読むことができます。

スレッドが並列構造に遭遇する、並列領域(..)を実行するためのスレッドのチームが作成されます。並列構造に遭遇したスレッドは、新しいチームのプライマリスレッドになり、新しい並列領域の期間中、スレッド番号はゼロになります。プライマリスレッドを含む、新しいチームのすべてのスレッドがリージョンを実行します。チームが作成されると、チーム内のスレッドの数は、その並列領域の間一定のままになります。

#pragma omp parallel for

作成されますparallel region(前述したように)、およびにthreadsその領域のそれが囲むことループの反復を使用して、割り当てられますdefault chunk size、そしてdefault scheduleこれは、典型的には static。ただし、標準のdefault schedule具体的な実装によって異なる場合があることに注意してOpenMPください。

OpenMP 5.1から、より正式な説明を読むことができます。

ワークシェアリングループ構造は、1つ以上の関連するループの反復が、暗黙のタスクのコンテキストでチーム内のスレッドによって並列に実行されることを指定します。反復は、ワークシェアリングループ領域がバインドする並列領域を実行しているチームにすでに存在するスレッドに分散されます。

また、

並列ループ構造は、1つ以上のループが関連付けられ、他のステートメントがないループ構造を含む並列構造を指定するためのショートカットです。

または非公式#pragma omp parallel forに、コンストラクター#pragma omp parallel#pragma omp for。の組み合わせです。あなたの場合、これは次のことを意味します。

#pragma omp parallel for
{
      for(int i=0;i<n;i++)
       {  
            c[i]=a[i]+b[i];
       }
}

意味的にも論理的にも、次と同じです。

#pragma omp parallel
{
      #pragma omp for
      for(int i=0;i<n;i++)
       {  
            c[i]=a[i]+b[i];
       }
}

TL; DR:この例では#pragma omp parallel for、ループはスレッド間で並列化されます(つまり、ループの反復はスレッド間で分割されます)が、#pragma omp parallel すべてのスレッドではすべてのループの反復が(並列に)実行されます。

よりわかりやすくするために、4スレッドを使用すると#pragma omp parallel、次のようになります。

一方#pragma omp parallel for、achunk_size=1static を使用すると、schedule次のようになります。

コード的には、ループは論理的に次のようなものに変換されます。

for(int i=omp_get_thread_num(); i < n; i+=omp_get_num_threads())
{  
    c[i]=a[i]+b[i];
}

ここでomp_get_thread_num()

omp_get_thread_numルーチンは、現在のチーム内で、呼び出し元のスレッドのスレッド番号を返します。

およびomp_get_num_threads()

現在のチームのスレッド数を返します。プログラムの順次セクションで、omp_get_num_threadsは1を返します。

つまり、for(int i = THREAD_ID; i < n; i += TOTAL_THREADS)THREAD_ID範囲0TOTAL_THREADS - 1、及びTOTAL_THREADS平行領域に作成チームのスレッドの合計数を表します。

forループでOpenMPを使用している間、#pragma omp parallelforを使用する必要があることを学びました。しかし、#pragma omp parallelでも同じことを試しましたが、正しい出力も得られます。

コード内で次の理由により、同じ出力が得られます。

 c[i]=a[i]+b[i];

配列aと配列bは読み取られるだけで、c[i]更新されるのは配列だけであり、その値は反復iが実行される回数に依存しません。それにもかかわらず、#pragma omp parallel for各スレッドでは独自のを更新しますがi#pragma omp parallelスレッドでは同じを更新するiため、互いの値をオーバーライドします。

次のコードで同じことを試してください。

#pragma omp parallel for
{
      for(int i=0;i<n;i++)
       {  
            c[i]= c[i] + a[i] + b[i];
       }
}

そして

#pragma omp for
{
      for(int i=0;i<n;i++)
       {  
            c[i] = c[i] + a[i] + b[i];
       }
}

すぐに違いに気付くでしょう。

2 DanielLangr Dec 11 2020 at 15:16

2番目のケースでは、ループは並列化されていません。つまり、ループ全体が各スレッドで実行されます。一般に、並列領域内にあるものはすべて、すべてのスレッドによって実行されます。

さらに、次のように、既存の並列領域でループを並列化できます。

#pragma omp parallel
{
  #pragma omp for
  for (int i = 0; i < n; i++)
    c[i] = a[i] + b[i];
}