web-dev-qa-db-fra.com

Comment OpenMP gère-t-il les boucles imbriquées?

Le code suivant ne fait-il que paralléliser les premières boucles (externes) ou parallélise-t-il l'intégralité des boucles imbriquées?

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

Je veux juste m'assurer que le code ci-dessus va paralléliser l'intégralité des boucles for imbriquées (donc un thread directement lié à la tâche (i, j)), ou s'il ne parallélise que la for-loop externe (ainsi il garantit que, pour chaque parallèle thread avec boucle index i, sa boucle interne se fera séquentiellement en un seul thread, ce qui est très important).

29
user0002128

Les lignes que vous avez écrites ne paralléliseront que la boucle externe. Pour paralléliser les deux, vous devez ajouter une clause 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)//
      }
    }

Vous pouvez vérifier OpenMP 3.1 spécifications (sec 2.5.1) pour plus de détails.

41
Massimiliano

Vous pourrez mieux comprendre cela avec l'exemple suivant. Faisons cela avec deux threads.

#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());
    }
}

alors le résultat sera,

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

Cela signifie que lorsque vous ajoutez #pragma omp parallel for à la boucle for la plus élevée, l'index de cette boucle for est divisé entre les threads. Comme vous pouvez le voir, lorsque l'index de i est le même, l'ID du thread est également le même.

Au lieu de cela, nous pouvons mettre en parallèle les combinaisons que nous avons dans une boucle imbriquée. Dans cet exemple, nous pouvons avoir les combinaisons suivantes de i et 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

Afin de paralléliser la combinaison de codes, nous pouvons ajouter le mot clé collapse comme suit.

#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());
    }
}

alors le résultat sera le suivant.

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 

Ensuite, vous pouvez voir que contrairement à avant, pour le même index i, il peut y avoir différents identifiants de threads (quand (i = 1 et j = 2 threadId = 1) également (i = 1 et j = 0 threadId = 0)). Cela signifie que dans ce scénario, les combinaisons de i et j sont divisées entre les threads.

6
Erangad

OpenMP ne fait que paralléliser la boucle à côté du pragma. Vous pouvez également paralléliser la boucle intérieure si vous le souhaitez, mais cela ne se fera pas automatiquement.

4
hcarver