gpt4 book ai didi

loops - 控制并行循环中的线程数并减少开销

转载 作者:行者123 更新时间:2023-12-04 20:33:12 25 4
gpt4 key购买 nike

在我的 Fortran 95 代码中,我有一系列嵌套的 DO 循环,整个循环需要大量时间来计算,所以我想用 OpenMP 添加并行功能(使用 gfortran -fopenmp 编译/构建)。

有一个主 DO 循环,运行 1000 次。

其中有一个子 DO 循环,运行 100 次。

其他几个DO循环嵌套在其中,迭代次数随着DO循环的每次迭代而增加(第一次,最后一次最多1000次)。

例子:

DO a = 1, 1000

DO b = 1, 100

DO c = 1, d
some calculations
END DO

DO c = 1, d
some calculations
END DO

DO c = 1, d
some calculations
END DO
END DO
d = d + 1
END DO

一些嵌套的 DO 循环必须串行运行,因为它们内部包含依赖关系(也就是说,循环的每次迭代都有一个包含前一次迭代的值的计算),并且在这种情况下不容易并行化.

我可以轻松地使没有任何依赖项的循环并行运行,如下所示:
d = 1
DO a = 1, 1000

DO b = 1, 100

DO c = 1, d
some calculations with dependencies
END DO
!$OMP PARALLEL
!$OMP DO
DO c = 1, d
some calculations without dependencies
END DO
!$OMP END DO
!$OMP END PARALLEL
DO c = 1, d
some calculations with dependencies
END DO
END DO
d = d + 1
END DO

但是我知道打开和关闭并行线程会有很大的开销,因为这在循环中发生了很多次。代码的运行速度明显慢于以前按顺序运行时的运行速度。

在此之后,我认为打开和关闭主循环两侧的并行代码(因此只应用一次开销)并将线程数设置为 1 或 8 以控制部分是按顺序运行还是按顺序运行是有意义的并行,如下:
d = 1
CALL omp_set_num_threads(1)
!$OMP PARALLEL
DO a = 1, 1000

DO b = 1, 100

DO c = 1, d
some calculations with dependencies
END DO
CALL omp_set_num_threads(4)
!$OMP DO
DO c = 1, d
some calculations without dependencies
END DO
!$OMP END DO
CALL omp_set_num_threads(1)

DO c = 1, d
some calculations with dependencies
END DO
END DO
d = d + 1
END DO
!$OMP END PARALLEL

但是,当我将它设置为运行时,我没有获得运行并行代码所期望的加速。我希望前几个会更慢来解释开销,但过了一段时间我希望并行代码比顺序代码运行得更快,但事实并非如此。我比较了主 DO 循环的每次迭代运行的速度,对于 DO a = 1, 50 ,结果如下:
Iteration    Serial    Parallel
1 3.8125 4.0781
2 5.5781 5.9843
3 7.4375 7.9218
4 9.2656 9.7500
...
48 89.0625 94.9531
49 91.0937 97.3281
50 92.6406 99.6093

我的第一个想法是我以某种方式没有正确设置线程数。

问题:
  • 我构建并行代码的方式有什么明显错误吗?
  • 有没有更好的方法来实现我所做的/想要做的?
  • 最佳答案

    确实有一些明显错误的地方:您已经从代码中删除了任何并行性。在创建最外面的并行区域之前,您将其大小定义为一个线程。因此,将只创建一个线程来处理该区域内的任何代码。随后使用 omp_set_num_threads(4)不会改变这一点。这个电话只是说下一个parallel指令将创建 4 个线程(除非另有明确要求)。但是没有这样的新 parallel指令,本应在此处 嵌套 在当前之一。您只有一个工作分享 do应用于当前封闭 parallel 的指令一个独特线程的区域。

    有两种方法可以解决您的问题:

  • 保持您的代码原样:尽管正式,您将在进入和退出 parallel 时 fork 并加入您的线程。区域,OpenMP 标准不要求创建和销毁线程。实际上,它甚至鼓励线程保持事件状态以减少 parallel 的开销。指令,这是由大多数 OpenMP 运行时库完成的。因此,这种简单的方法的问题的有效载荷并不太大。
  • 使用第二种方法来插入 parallel指令在最外层循环之外,但是创建尽可能多的线程以进行工作共享(我相信这里是 4 个)。然后,您将所有必须按顺序排列的内容包含在 parallel 中。具有 single 的区域指示。这将确保不会发生与额外线程的不需要的交互(隐式屏障和退出时共享变量的刷新),同时避免您不想要的并行性。

  • 最后一个版本看起来像这样:
    d = 1
    !$omp parallel num_threads( 4 ) private( a, b, c ) firstprivate( d )
    do a = 1, 1000
    do b = 1, 100
    !$omp single
    do c = 1, d
    some calculations with dependencies
    end do
    !$omp end single
    !$omp do
    do c = 1, d
    some calculations without dependencies
    end do
    !$omp end do
    !$omp single
    do c = 1, d
    some calculations with dependencies
    end do
    !$omp end single
    end do
    d = d + 1
    end do
    !$omp end parallel

    现在,与天真的版本相比,此版本实际上是否更快,由您来测试。

    不过最后要说的是:由于您的代码中有相当多的连续部分,所以无论如何不要期望有太多的加速。 Amdahl's law是永远。

    关于loops - 控制并行循环中的线程数并减少开销,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41273885/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com