使用OpenMP正确/高效地并行化for循环

时间:2017-07-29 21:58:47

标签: multithreading for-loop parallel-processing openmp

我开发了一个分布式内存MPI应用程序,它涉及网格的处理。现在我想使用OpenMP来应用共享内存技术(基本上使它成为混合 - 并行程序),看它是否可以变得更快或更高效。我在使用OpenMP时遇到了困难,尤其是嵌套for循环。我的应用程序涉及每隔半秒将网格打印到屏幕,但是当我将其与OpenMP并行化时,执行速度会慢10倍,或者根本不会。控制台屏幕滞后并使用随机/意外数据刷新自身。换句话说,它完全错了。看看下面的函数,它执行打印:

void display2dGrid(char** grid, int nrows, int ncolumns, int ngen)
{
    //#pragma omp parallel
    updateScreen();
    int y, x;
    //#pragma omp parallel shared(grid)      // garbage
    //#pragma omp parallel private(y)        // garbage output!
    //#pragma omp for
    for (y = 0; y < nrows; y++) {
        //#pragma omp parallel shared(grid)  // nothing?
        //#pragma omp parallel private(x)    // 10 times slower!
        for (x = 0; x < ncolumns; x++) {
            printf("%c ", grid[y][x]);
        }
        printf("\n");
    }
    printf("Gen #%d\n", ngen);
    fflush(stdout);
}

(updateScreen()只清除屏幕并再次从左上角写入。)

该函数仅由一个进程执行,这使其成为线程并行化的理想目标。正如你所看到的,我已经尝试了很多方法,其中一种比另一种更糟糕。最好的情况是,我每2秒获得半输出(因为它刷新非常缓慢)。最坏的情况我得到垃圾输出。

我将不胜感激任何帮助。有没有一个地方可以找到更多信息来正确并行化OpenMP循环?提前谢谢。

1 个答案:

答案 0 :(得分:2)

  

该函数仅由一个进程执行,这使其成为线程并行化的理想目标。

实际上并非如此。您尝试并行化的功能是并行化的一个非常糟糕的目标。您示例中对printf的调用需要按照特定的顺序发生,否则,您将获得有经验的垃圾结果(,因为您的网格元素将按照无意义的顺序打印)。实际上,你在并行化方面的尝试非常好,问题来自于函数本身是并行化的坏目标。

并行化程序时的加速来自于您在多个核心之间分配工作负载的事实。为了能够以最高效率实现这一目标,所述工作负载需要独立,或者至少尽可能少地共享状态,这不是这里的情况对printf的调用需要按特定顺序进行。

当你尝试并行化一些内在顺序的工作时,你会失去更多时间synchronizing你的工作者(你的openmp线程),而不是你通过平行工作本身而获得的(这就是你获得废话时间的原因)结果变得更好。)

另外,正如这个答案(https://stackoverflow.com/a/20089967/3909725)所暗示的,你不应该在每个循环中打印网格的内容(除非你正在调试),而是执行所有的计算,然后打印内容时你已经完成了你的最终目标,因为打印只对查看计算结果有用,并且只会减慢过程。

一个例子:

这是一个使用openmp对程序进行并行化以实现加速的非常基本的示例。这里为i变量的每个值实现了一个虚拟(但很重)的计算。每个循环中的计算是完全独立的,并且不同的线程可以独立地实现它们的计算。对printf的调用可以按任何顺序实现,因为它们只是提供信息。

原创(sequential.c)

#include <math.h>
#include <stdio.h>
#include <stdlib.h>


int main()
{
  int i,j;
  double x=0;

  for(i=0; i < 100; i++)
    {
      x = 100000 * fabs(cos(i*i));
      for(j=0;j<100+i*20000;j++)
        x += sqrt(sqrt(543*j)*fabs(sin(j)));
      printf("Computed i=%2d [%g]\n",i,x);
    }
}

并行化版本(parallel.c)

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

int main()
{
  int i,j;
  double x=0;
#pragma omp parallel for
  for(i=0; i < 100; i++)
    {
      /* Dummy heavy computation  */
      x = 100000 * fabs(cos(i*i));
      #pragma omp parallel for reduction(+: x)
      for(j=0;j<100+i*20000;j++)
        x += sqrt(sqrt(543*j)*fabs(sin(j)));

      printf("Thread %d computed i=%2d [%g]\n",omp_get_thread_num(),i,x);
    }
}

可以在这里找到一个很好的openmp指南:http://bisqwit.iki.fi/story/howto/openmp/