【问题标题】:Proper / Efficient parallelization of a for loop with OpenMP使用 OpenMP 正确/高效地并行化 for 循环
【发布时间】:2017-07-29 21:58:46
【问题描述】:

我开发了一个涉及网格处理的分布式内存 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 正确并行化循环?提前致谢。

【问题讨论】:

  • 我理解正确吗?您希望多个线程同时写入(某种程度)到控制台而不是垃圾?您希望多个线程争夺对单个通道的访问权以写入屏幕并让这个通道愉快地进行?
  • 如果您使用的序列随机数生成器也会破坏并行性。

标签: multithreading for-loop parallel-processing openmp


【解决方案1】:

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

这实际上是不正确的。您尝试并行化的函数是一个非常糟糕的并行化目标。在您的示例中对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/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-23
    • 2013-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多