减少openmp并行延迟的措施

Measures to mitigate openmp parallel latency

本文关键字:延迟 openmp 并行 减少      更新时间:2023-10-16

我是从Fortran的角度来写这个问题的,但这些问题并不局限于Fortran(因此有c++标签)。

我有两个问题。我读到在OpenMP并行循环的开始和结束处存在延迟。我的问题是:

Q1)有哪些实际措施可以减少openMP延迟?

Q2)以下哪种方法性能更好?

方法1

 x = 1.0; y = 2.0
 !$OMP PARALLEL DO
 do k=1,Nz; do j=1,Ny; do i=1,Nx
 x(i,j,k) = x(i,j,k)+y(i,j,k)
 enddo; enddo; enddo
 !$OMP END PARALLEL DO
 !$OMP PARALLEL DO
 do k=1,Nz; do j=1,Ny; do i=1,Nx
 x(i,j,k) = x(i,j,k)*y(i,j,k)
 enddo; enddo; enddo
 !$OMP END PARALLEL DO
 ! (x should = 6.0 at this point)

方法2

 x = 1.0; y = 2.0
 !$OMP PARALLEL DO
 do k=1,Nz; do j=1,Ny; do i=1,Nx
 x(i,j,k) = x(i,j,k)+y(i,j,k)
 x(i,j,k) = x(i,j,k)*y(i,j,k)
 enddo; enddo; enddo
 !$OMP END PARALLEL DO
 ! (x should = 6.0 at this point)
方法3

1)创建一个包含过程数组的对象

2)按如下方式调用过程数组

 x = 1.0; y = 2.0
 !$OMP PARALLEL DO
 do k=1,Nz; do j=1,Ny; do i=1,Nx
 do t=1,procedure_array%N
 call procedure_array%single_procedure(t)%P(x(i,j,k),y(i,j,k))
 enddo
 enddo; enddo; enddo
 !$OMP END PARALLEL DO
 ! (x should = 6.0 at this point)

假设procedure_array%N = 2

 procedure_array%single_procedure(1)
 procedure_array%single_procedure(2)

分别指向子程序add (x=x+y)和multiply (x=x*y)。

评论>

首先,很明显方法2优于方法1,所以我对方法2和方法3的比较很感兴趣。其次,我知道"试一试看看"是一个有效的答案,但我想知道是否有具体的例子说明方法3在实践中(或工业中)被使用,或者为什么方法3比方法2差的概念原因(例如,由于许多过程调用造成的开销)。最后,如果可以采取某种特殊的措施(例如,通过特别指定特定的线程)使方法2和方法3实际上相等,那么它们是什么?

谢谢你的帮助!

根据评论,我做了以下更正。

  • 修改了操作(谢谢,@Gilles)
  • 删除了所有与MPI相关的内容,这真的是一个开放的mp问题(谢谢你,@Vladimir)
  • 在下面添加了澄清(我自己意识到)

谢谢你,@innoSPG,关于你关于缓存内存(和缓存故障)的答案,这是非常有益的信息!

最后,从评论中,我意识到这个问题的重点实际上是关于过程调用,而不是严格地与openmp并行化相关。也就是说,我留下了openmp语句,因为这是在我的更复杂的应用程序中发生的事情,我希望尽可能地保留它。从@Chaosit的评论看来,过程调用将需要开销,减慢方法3。有什么办法可以绕过这个吗?

如果我错了,请纠正我,但我相信方法2中的两个操作将按照书面顺序执行,从而产生正确的最终x值。

我的理解是,您正在寻找的差异(方法2和方法3)并不取决于并行化。

让我解释一下:

方法3可能会由于方法2中不存在的过程调用而产生另一个开销。我说可能是因为我不熟悉过程指针,但是,我的猜测是,如果您使用过程指针,编译器的优化将不会内联过程调用。

过程调用的开销与并行化完全无关。您仍然会在顺序代码中拥有它。它不是并行化延迟的一部分。

现在,我要冒险回到你不喜欢的选项:试着看看。我之所以冒这个险,是因为我相信你所展示的只是你的问题的一个例证,你的实际情况要复杂得多。当你遇到复杂的情况时,你可能会惊讶地发现方法1和方法2之间的结论不再有效。这很大程度上取决于你手头的问题。一种情况下的结论不适用于另一种情况。尽管现在的编译器很聪明,但它们不会捕获所有内容。在计算机科学中,有一个缓存内存的概念有时不是很好理解,但我不会详细介绍。可以有这样一种情况,方法1的每个单循环(数据和/或代码)保存在缓存中,而情况2的组合循环不保存在缓存中。最简单的例子是方法2在内部循环中遇到缓存错误,而方法1没有。在这种情况下,您将看到对于大循环边界,方法1远远优于方法2。

这就是Try and see成为标准的地方,这就是现实生活中大多数情况下发生的情况。