我正在尝试并行化这种循环
#pragma omp parallel for private(i,j)
for(i=0;i<N;i++){ //N is very large
for(j=0;j<i;j++){
//do some work here
}
}
使用OpenMP指令。
主要问题是当i
变大时,最里面的循环还有更多工作要做。因为默认情况下,循环通过为每个线程分配一个连续的主循环迭代块来并行化(即如果使用2个线程,则只有thread1被赋值i = 0..N / 2-1并且thread2被赋值i = N /2..N-1)最后一个线程总是比第一个线程有更多的工作,导致线程之间的负载不平衡。
我已经读过控制这种行为的一种方法是通过Scheduling子句schedule(type,chunk)
,其中type
可以是静态的,动态的或引导的。问题是我不理解schedule
子句及其不同类型的行为,因此我无法正确使用它。 (说明性的例子可以做到这一点)
特别是,我有兴趣为每个线程分配交替的迭代块,以便所有这些迭代从i
的较低范围接收迭代,只要从其较高的范围开始,就等于每个线程都在做。例如,如果使用4个线程,我希望这样的事情发生
thread1: i=0,4,8...
thread2: i=1,5,9...
thread3: i=2,6,10...
thread4: i=3,7,11...
虽然也欢迎其他解决问题的方法。
答案 0 :(得分:2)
当你有内部循环依赖于外部循环的迭代次数时,我认为你想要使用schedule(guided)
。 编辑:我现在认为`schedule(static,1)可能是最好的,因为它平衡负载并且比动态调度程序具有更少的开销。请参阅答案的结尾。我的意思是
for(i=0;i<N;i++) {
for(j=0;j<i;j++){
//do some work here
}
}
或
for(i=0;i<N-1;i++) {
for(j=i+1;j<N;j++){
//do some work here
}
}
这两个都在数字三角形(自上而下或自下而上)上运行,并按顺序进行N *(N + 1)/ 2次迭代。让我们看一下当你在代码中使用schedule(static)
和2个线程代替N = 8时会发生什么。
schedule (static): thread 1 i = 0-3, thread 2 i = 4-7
thread 1: chunck_size = 4
i = 1, j = 0
i = 2, j = 0, 1
i = 3, j = 0, 1, 2
iterations = 1+2+3 = 6
thread 2: chunck_size = 4
i = 4, j = 0, 1, 2, 3
i = 5, j = 0, 1, 2, 3, 4
i = 6, j = 0, 1, 2, 3, 4, 5
i = 7, j = 0, 1, 2, 3, 4, 5, 6
iterations = 4+5+6+7 = 22
第一个线程执行6次迭代,第二个线程执行22次迭代,因此负载不平衡(假设每次迭代的工作相等)。现在让我们看看schedule(guided)
。引导每个连续的线程获得number_of_iterations_remaining / number_of_threads
。
schedule (guided):
thread 1: chunk size = 8/2 = 4
i = 1, j = 0
i = 2, j = 0, 1
i = 3, j = 0, 1, 2
iterations = 1+2+3 = 6
thread 2: chunk size = 4/2 = 2
i = 4, j = 0, 1, 2, 3
i = 5, j = 0, 1, 2, 3, 4
iterations = 4+5 = 9
thread 1: chunk size = 2/2 = 1
i = 6, j = 0, 1, 2, 3, 4, 5
iterations = 6
thread 2: chunk size = 1
i = 7, j = 0, 1, 2, 3, 4, 5, 6
iterations = 7
现在负载更好平衡。随着i
增加迭代次数j
以增加,但随着guided
,每个线程的块大小减小,因此这两个事物的组合使每个迭代次数变得平缓线程。
编辑:
schedule (static,1):
thread 1: i = 1, j = 0
thread 2: i = 2, j = 0, 1
thread 1: i = 3, j = 0, 1, 2
thread 2: i = 4, j = 0, 1, 2, 3
thread 1: i = 5, j = 0, 1, 2, 3, 4
thread 2: i = 6, j = 0, 1, 2, 3, 4, 5
thread 1: i = 7, j = 0, 1, 2, 3, 4, 5, 6
线程1执行1 + 3 + 5 + 7 = 16次迭代
线程2做2 + 4 + 6 = 12次迭代
如果我们定义n = N/num_threads
,那么通常线程1执行n^2
次迭代(因为奇数的总和是一个正方形),而线程2将进行(n-1)*n
次迭代 。在这种情况下,n = 4.因此,线程1执行4 ^ 2 = 16次迭代,而thread2执行3 * 4 = 12次迭代,因此我们看到公式是正确的。例如,如果N = 200且num_threads = 2,则thread1将执行100 ^ 2 = 10000次迭代,而thread2将执行99 * 100 = 9900次迭代。所以负载或多或少是平衡的。
答案 1 :(得分:1)
要获得您的目标,您应该使用:
#pragma omp parallel for private(i,j) schedule(static,1)
for(i=0;i<N;i++){ //N is very large
for(j=0;j<i;j++){
//do some work here
}
}
实际上,根据标准(OpenMP 4.0):
当指定 schedule(static,chunk_size)时,将分割迭代次数 进入大小为chunk_size的块,并将块分配给 团队中的线程以循环方式按顺序排列 线号。
除此之外,我建议尝试dynamic
和guided
调度,这会将平衡逻辑从静态(和可重现的)映射移动到运行时系统。