web-dev-qa-db-ja.com

OpenMPはネストされたループをどのように処理しますか?

次のコードは、最初の(外側の)ループを単に並列化しますか、それともネストされたループ全体を並列化しますか?

    #pragma omp parallel for
    for (int i=0;i<N;i++)
    { 
      for (int j=0;j<M;j++)
      {
       //do task(i,j)//
      }
    }

上記のコードがネストされたforループ全体を並列化する(つまり、1つのスレッドが直接関連するtask(i、j))か、外側のforループのみを並列化する(したがって、各並列に対してループインデックスiのスレッド、その内部ループは単一のスレッドで順次実行されます。これは非常に重要です)。

29
user0002128

記述した行は、外側のループのみを並列化します。両方を並列化するには、collapse句を追加する必要があります。

#pragma omp parallel for collapse(2)
    for (int i=0;i<N;i++)
    { 
      for (int j=0;j<M;j++)
      {
       //do task(i,j)//
      }
    }

詳細については、 OpenMP 3.1 仕様(秒2.5.1)を確認してください。

41
Massimiliano

次の例でこれをよりよく理解できます。これを2つのスレッドで行いましょう。

#pragma omp parallel for num_threads(2)
for(int i=0; i< 3; i++) {
    for (int j=0; j< 3; j++) {
        printf("i = %d, j= %d, threadId = %d \n", i, j, omp_get_thread_num());
    }
}

結果は次のようになります。

i = 0, j= 0, threadId = 0 
i = 0, j= 1, threadId = 0 
i = 0, j= 2, threadId = 0 
i = 1, j= 0, threadId = 0 
i = 1, j= 1, threadId = 0 
i = 1, j= 2, threadId = 0 
i = 2, j= 0, threadId = 1 
i = 2, j= 1, threadId = 1 
i = 2, j= 2, threadId = 1

つまり、#pragma omp parallel forを最上部のforループに追加すると、そのforループのインデックスはスレッド間で分割されます。ご覧のとおり、iのインデックスが同じ場合、スレッドIDも同じです。

その代わりに、ネストされたforループにある組み合わせを並列化できます。この例では、次のiとjの組み合わせを使用できます。

i = 0, j= 0
i = 0, j= 1
i = 0, j= 2
i = 1, j= 0
i = 1, j= 1
i = 1, j= 2
i = 2, j= 0
i = 2, j= 1
i = 2, j= 2

コードの組み合わせを賢明に並列化するために、次のようにcollapseキーワードを追加できます。

#pragma omp parallel for num_threads(2) collapse(2)
for(int i=0; i< 3; i++) {
    for (int j=0; j< 3; j++) {
        printf("i = %d, j= %d, threadId = %d \n", i, j, omp_get_thread_num());
    }
}

結果は次のようになります。

i = 0, j= 0, threadId = 0 
i = 0, j= 1, threadId = 0 
i = 1, j= 2, threadId = 1 
i = 2, j= 0, threadId = 1 
i = 2, j= 1, threadId = 1 
i = 2, j= 2, threadId = 1 
i = 0, j= 2, threadId = 0 
i = 1, j= 0, threadId = 0 
i = 1, j= 1, threadId = 0 

次に、以前とは異なり、同じインデックスiに対して異なるスレッドIDが存在することがわかります((i = 1およびj = 2 threadId = 1)の場合(i = 1およびj = 0 threadId = 0))。つまり、このシナリオでは、iとjの組み合わせがスレッド間で分割されます。

6
Erangad

OpenMPは、プラグマの隣のループのみを並列化します。必要に応じて、内側のループも並列化できますが、自動的には実行されません。

4
hcarver