56

下面的代码只是并行化第一个(外部)循环,还是并行化整个嵌套循环?

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

我只是想确定上面的代码是否会并行化整个嵌套的for循环(因此一个线程直接相关的任务(i,j)),或者它只并行化外部for循环(因此它确保对于每个parallel循环索引为 i 的线程,其内部循环将在单个线程中顺序完成,这非常重要)。

4

2 回答 2

68

您编写的行将仅并行化外部循环。要并行化两者,您需要添加一个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 节)以了解更多详细信息。

于 2012-11-13T07:51:37.103 回答
25

通过以下示例,您将能够更好地理解这一点。让我们用两个线程来做这件事。

#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

为了明智地并行化代码组合,我们可以添加折叠关键字,如下所示。

#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 and j=2 threadId=1) 也 (i=1 and j=0 threadId=0))。这意味着在这种情况下,i 和 j 的组合在线程之间进行划分。

于 2018-05-22T10:36:25.183 回答