OpenMP上的工作不平衡

时间:2014-01-07 08:02:42

标签: c openmp

我正在尝试并行化这种循环

#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...

虽然也欢迎其他解决问题的方法。

2 个答案:

答案 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的块,并将块分配给   团队中的线程以循环方式按顺序排列   线号。

除此之外,我建议尝试dynamicguided调度,这会将平衡逻辑从静态(和可重现的)映射移动到运行时系统。