OpenMP显著减慢for循环的速度

3
我尝试使用OpenMP并行化加速此for循环。 我原本以为这应该将工作分散到多个线程上。 然而,可能开销太大了,无法提供任何加速。 我应该提到,这个循环会发生很多很多次,每个循环实例都应该是并行的。 循环迭代次数newNx可以小至3或大至256。 但是,如果我有条件地仅在newNx> 100(仅最大的循环)时并行化它,它仍然会显着减慢。 这里是否有任何因素导致速度变慢? 我还应该提到,向量A,v,b非常大,但我认为访问是O(1)。
    #pragma omp parallel for private(j,k),shared(A,v,b)
    for(i=1;i<=newNx;i+=2) {
      for(j=1;j<=newNy;j++) { 
        for(k=1;k<=newNz;k+=1) {

          nynz=newNy*newNz; 

          v[(i-1)*nynz+(j-1)*newNz+k] = 
          -(v[(i-1)*nynz+(j-1)*newNz+k+1 - 2*(k/newNz)]*A[((i-1)*nynz + (j-1)*newNz + (k-1))*spN + kup+offA] + 
          v[(i-1)*nynz+(j-1)*newNz+ k-1+2*(1/k)]*A[((i-1)*nynz + (j-1)*newNz + (k-1))*spN + kdo+offA] + 
          v[(i-1)*nynz+(j - 2*(j/newNy))*newNz+k]*A[((i-1)*nynz + (j-1)*newNz + (k-1))*spN + jup+offA] + 
          v[(i-1)*nynz+(j-2 + 2*(1/j))*newNz+k]*A[((i-1)*nynz + (j-1)*newNz + (k-1))*spN + jdo+offA] + 
          v[(i - 2*(i/newNx))*nynz+(j-1)*newNz+k]*A[((i-1)*nynz + (j-1)*newNz + (k-1))*spN + iup+offA] + 
          v[(i-2 + 2*(1/i))*nynz+(j-1)*newNz+k]*A[((i-1)*nynz + (j-1)*newNz + (k-1))*spN + ido+offA] - 
          b[(i-1)*nynz + (j-1)*newNz + k])
          /A[((i-1)*nynz + (j-1)*newNz + (k-1))*spN + ifi+offA];}}}

5
我也得大幅减缓阅读速度才能读懂这些... 我不会责怪OpenMP... - Kerrek SB
1
只有五个字母:IOCCC(说真的,如果你花点功夫让这段代码更易读,也许会有人给你一些提示) - Massimiliano
OpenMP非常适合处理高度并行的负载,这意味着循环的每个迭代都必须与所有其他迭代无关。其次,您需要有足够的迭代和足够大的工作量才能使其值得,即使有许多迭代,每个迭代执行速度很快的小循环通常也不值得。 - Mgetz
基本上是对三个大数组进行一堆数组调用,如果你不想读它的话。 - user2770042
1
检查您的迭代计数... OpenMP 对此无济于事,要让 OpenMP 起作用,您需要更高的迭代计数,并且循环的每个迭代都需要更加重要。您可能会遇到其他问题,例如引用的局部性。我强烈建议运行分析器来找出需要优化的内容,而不是盲目地进行优化。 - Mgetz
显示剩余2条评论
1个回答

6
假设你没有竞争条件,那么你可以尝试融合循环。融合会给并行化提供更大的块,这有助于减少错误共享的影响,并可能更好地分配负载。
对于像这样的三重循环:
for(int i2=0; i2<x; i2++) {
    for(int j2=0; j2<y; j2++) {
        for(int k2=0; k2<z; k2++) {
            //
        }
    }
}

你可以像这样融合它。
#pragma omp parallel for
for(int n=0; n<(x*y*z); n++) {
    int i2 = n/(y*z);
    int j2 = (n%(y*z))/z;
    int k2 = (n%(y*z))%z;
    //
}

在您的情况下,您可以像这样做。
int i, j, k, n;
int x = newNx%2 ? newNx/2+1 : newNx/2;
int y = newNy;
int z = newNz;

#pragma omp parallel for private(i, j, k)
for(n=0; n<(x*y*z); n++) {
    i = 2*(n/(y*z)) + 1;
    j = (n%(y*z))/z + 1;
    k = (n%(y*z))%z + 1;
    // rest of code
}

如果这样做确实加速了您的代码,那么您会感到很高兴,因为您不仅使代码更快,而且进一步混淆了它。

1
+1 只是因为你尝试阅读那段代码。使用“collapse”指令也可以实现融合,不是吗? - rath
1
是的,但那需要一个 MSVC 不支持的 OpenMP 版本,而且我喜欢有适用于多个编译器的代码,所以我自己进行融合。 - Z boson
建议(需要math.h库):int x = ceil(newNx/2); - ofer.sheffer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接