我正在使用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使用率的最佳方法是什么?
答案 0 :(得分:4)
一如既往:
这些将会产生影响的事情没有显示出来:
关于你的上一个场景:
for(int i = 0; i < N; i++) {
for(int k = i; k < N; k++) {
// k starts from i, not from 0...
}
}
我建议并行化外部循环,原因如下:
所有其他条件相同的粗粒度并行化通常会导致更好的性能
内部循环可能变得如此短以至于无效并行化(IOW:外部循环的范围是可预测的,内部循环不那样,或者也不适合静态调度)
答案 1 :(得分:2)
sehe的要点 - 特别是“它取决于”和“简介” - 非常重要。
但是,通常情况下,只要外部循环足够大以保持所有核心繁忙,您就不希望拥有嵌套的并行循环。另一个并行部分在循环中的额外开销可能比从额外的小部分工作中获得的成本更高。
解决这个问题的常用方法就是动态调度外部循环,以便每个循环迭代采用不同类型的长度这一事实不会导致负载平衡问题(因为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...
}
}
崩溃编译指示对于基本上摆脱嵌套非常有用,如果外环很小(例如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
)。