OpenMP嵌套的迭代次数不等

OpenMP nested for, unequal num. of iterations

本文关键字:迭代 嵌套 OpenMP      更新时间:2023-10-16

我正在使用OpenMP并行循环。在正常情况下,可以使用:

#pragma omp for schedule(static, N_CHUNK)
for(int i = 0; i < N; i++) {
    // ...
}

对于嵌套循环,我可以将pragma放在内部或外部循环

#pragma omp for schedule(static, N_CHUNK) // can be here...
for(int i = 0; i < N; i++) {
#pragma omp for schedule(static, N_CHUNK) // or here...
    for(int k = 0; k < N; k++) {
    // both loops have consant number of iterations
    // ...
    }
}

但是!我有两个循环,其中第二个循环的迭代次数取决于第一个循环:

for(int i = 0; i < N; i++) {
    for(int k = i; k < N; k++) {
    // k starts from i, not from 0...
    }
}
对于这种循环,平衡CPU使用的最佳方法是什么?

一如既往:

  • 这取决于
  • 概要文件。
  • 在这种情况下:参见OMP_NESTED环境变量

将在这里产生差异的东西没有显示:

  • (非线性)内存寻址(同时注意循环的顺序
  • )共享变量的使用

关于你的最后一个场景:

for(int i = 0; i < N; i++) {
    for(int k = i; k < N; k++) {
    // k starts from i, not from 0...
    }
}

我建议并行化外部循环,原因如下:

  • 在其他条件相同的情况下,粗粒度并行化通常会带来更好的性能,因为

    • 增加缓存局部性
    • 减少了所需的锁定频率(注意,这取决于关于循环内容的假设,我不能真正做到;我是基于我的经验/通常/并行代码)
  • 内部循环可能变得太短,以至于无法并行化(低:外部循环的范围是可预测的,内部循环的范围则不那么可预测,或者也不适合静态调度)

  • 嵌套并行很少能很好地伸缩

她的观点——尤其是"视情况而定"answers"个人资料"——非常切题。

通常情况下,只要外部循环大到足以让所有内核都忙起来,你就不会想要嵌套并行循环。在循环内的另一个并行段所增加的开销可能比额外的小块工作所带来的好处要多。

通常解决这个问题的方法是动态调度外部循环,这样每个循环迭代使用不同长度的类型就不会导致负载平衡问题(因为i==N-1迭代几乎立即完成,而i==0迭代需要永远)

#pragma omp parallel for default(none) shared(N) schedule(dynamic)
for(int i = 0; i < N; i++) {
    for(int k = i; k < N; k++) {
    // k starts from i, not from 0...
    }
}

collapse pragma对于从本质上摆脱嵌套非常有用,如果外部循环很小(例如N < num_threads),则特别有价值:

#pragma omp parallel for default(none) shared(N) collapse(2)
for(int i = 0; i < N; i++) {
    for(int k = 0 ; k < N; k++) {
    }
}

这样两个循环被折叠成一个,并且有更少的分块,这意味着更少的开销。但这在这种情况下不起作用,因为循环范围不是固定的;你不能collapse一个循环边界改变的循环(例如,用i)。