【问题标题】:Understanding the collapse clause in openmp理解openmp中的collapse子句
【发布时间】:2015-04-13 12:07:36
【问题描述】:

我遇到了一个包含折叠子句的 OpenMP 代码,这对我来说是新的。我试图理解它的含义,但我认为我没有完全理解它的含义;我发现的一个定义是:

COLLAPSE:指定嵌套循环中有多少个循环应该被折叠到一个大的迭代空间中,并根据调度子句进行划分。所有关联循环中迭代的顺序执行决定了折叠迭代空间中迭代的顺序。

我以为我明白这意味着什么,所以我尝试了以下简单程序:

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

生产的

0 0 0
1 0 0
1 1 0
2 0 0
2 1 0
2 2 1
3 0 1
3 1 1
3 2 1
3 3 1

然后我添加了collapse(2) 子句。我希望在前两列中得到相同的结果,但现在在最后一列中有相同数量的 01。 但我得到了

0 0 0
1 0 0
2 0 1
3 0 1

所以我的问题是:

  1. 我的代码发生了什么?
  2. 什么情况下应该使用collapse
  3. 您能否提供一个示例来说明使用collapse 与不使用collapse 之间的区别?

【问题讨论】:

  • 好问题。您正在尝试融合三角形双环。我不认为崩溃适用于此。它需要是一个方形双环。 Others on SO have said collapse works with triangular loops。我还没有阅读规范。如果您想融合一个三角形循环,请查看此question。虽然,我现在知道使用归纳变量的更好方法。
  • 但是如果是方形双循环,使用collapse有什么好处呢?无论哪种方式,每个线程都将获得相同数量的迭代。
  • 如果你在折叠之前有两个嵌套循环在nm 上,每个线程都会得到n/nthreads 迭代,而在你折叠之后它是n*m 迭代。这可以帮助例如当n 相对于nthreads 不是很大但n*m 是。
  • 如果您使用 C99,它可以省去私有化循环索引的麻烦... #pragma omp parallel for for (int i = 0; i
  • 当前未折叠的输出不正确,每个线程显示 5 个输出——对于线程 #0,应该只为外部循环值 0 和 2(即 0 0 0、2 0 0、2 1 0 ) 其他输出应该是线程#1。

标签: c openmp


【解决方案1】:

如果您的目的是在增加的行上平衡负载,假设每个项目的工作负载是规则的或分散良好的,那么将行索引对折,忘记collapse 子句怎么样?

#pragma omp for
for (int iy0=0; iy0<n; ++iy0){
  int iy = iy0;
  if (iy0 >= n/2) iy = n-1 -iy0 +n/2;
  for (int ix=iy+1; ix<n; ++ix){
    work(ix, iy);
  }
}

【讨论】:

    【解决方案2】:

    您的代码的问题是内循环的迭代取决于外循环。根据绑定部分和collapse 子句描述下的OpenMP 规范:

    如果任何关联循环的执行更改了用于计算任何值的任何值 的迭代计数,则行为未指定。

    如果不是这种情况,您可以使用折叠,例如方形循环

    #pragma omp parallel for private(j) collapse(2)
    for (i = 0; i < 4; i++)
        for (j = 0; j < 100; j++)
    

    事实上,这是一个很好的例子来说明何时使用折叠。外循环只有四次迭代。如果您有四个以上的线程,那么有些线程将被浪费。但是当您折叠时,线程将分布在 400 次迭代中,这可能远远大于线程数。使用崩溃的另一个原因是负载分布不均。如果您只使用了四次迭代,而第四次迭代花费了其他线程等待的大部分时间。但如果您使用 400 次迭代,负载可能会更好地分布。

    您可以像这样为上面的代码手动融合一个循环

    #pragma omp parallel for
    for(int n=0; n<4*100; n++) {
        int i = n/100; int j=n%100;
    

    Here 是一个示例,展示了如何手动融合三重融合循环。

    最后,here 是一个示例,展示了如何融合未定义 collapse 的三角形循环。


    这是一个将矩形循环映射到 OPs 问题中的三角形循环的解决方案。这可以用来融合 OPs 三角环。

    //int n = 4;
    for(int k=0; k<n*(n+1)/2; k++) {
        int i = k/(n+1), j = k%(n+1);
        if(j>i) i = n - i -1, j = n - j;
        printf("(%d,%d)\n", i,j);
    }
    

    这适用于任何 n 值。

    OPs 问题的地图来自

    (0,0),
    (1,0), (1,1),
    (2,0), (2,1), (2,2),
    (3,0), (3,1), (3,2), (3,3),
    

    (0,0), (3,3), (3,2), (3,1), (3,0),
    (1,0), (1,1), (2,2), (2,1), (2,0),
    

    对于 n 的奇数值,地图并不完全是一个矩形,但公式仍然有效。

    例如 n = 3 从

    得到映射
    (0,0),
    (1,0), (1,1),
    (2,0), (2,1), (2,2),
    

    (0,0), (2,2), (2,1), (2,0),
    (1,0), (1,1),
    

    这是测试的代码

    #include <stdio.h>
    int main(void) {
        int n = 4;
        for(int i=0; i<n; i++) {
            for(int j=0; j<=i; j++) {
                printf("(%d,%d)\n", i,j);
            }
        }
        puts("");
        for(int k=0; k<n*(n+1)/2; k++) {
            int i = k/(n+1), j = k%(n+1);
            if(j>i) i = n - i - 1, j = n - j;
            printf("(%d,%d)\n", i,j);
        }
    }
    

    【讨论】:

    • @Gilles,你为什么要在我的回答中添加评论 &lt;!-- language-all: lang-c --&gt;?这样做有什么意义。我不是在抱怨。我只是不知道它是干什么用的。
    • 我刚刚添加了 C 语法高亮提示,如 here 所述。事实上,在我的浏览器上,你所有的代码 sn-ps 都显示为灰色。现在,至少在我的浏览器上,但我猜在其他许多浏览器上,C 语法都是彩色的。好的,输出 sn-ps 中的索引也是如此,这可能是不需要的,但如果您愿意,可以修复它吗?无论如何,我不想闯入,但我认为一个好的答案应该有好的颜色......我走得太远了吗?
    • @Gilles,我不知道这一点。谢谢!我完全不介意你改进了我的答案。
    • 但是我没明白这个参数是什么意思?崩溃(2)什么是 2 ?!
    • @N0rA 循环数。 collapse(n) 将以下 n 嵌套循环折叠成线程共享的单个并行循环。
    猜你喜欢
    • 1970-01-01
    • 2017-09-26
    • 1970-01-01
    • 2011-07-01
    • 2021-08-13
    • 2012-04-26
    • 1970-01-01
    • 1970-01-01
    • 2015-01-25
    相关资源
    最近更新 更多