我从Fortran的角度编写了这个问题,但问题并不仅限于Fortran(因此是c ++标签)。
我有两个问题。我已经读过在OpenMP并行循环here的开始和结束时存在延迟。我的问题是:
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
)。
3)清理(解除分配)
评论
首先,很明显方法2优于方法1,所以我真的对方法2与方法3的比较感兴趣。其次,我知道“尝试看看”是一个有效的答案,但是我想知道方法3是否在实践(或行业)中使用的具体示例或概念性原因为什么方法3与方法2相比较差(例如由于许多过程调用引起的开销)。最后,如果可以采取某种特殊关注(例如,通过专门指定特定线程)来使方法2和3几乎相同,那么它们是什么?
我感谢任何帮助!
更新
根据评论,我做了以下更正。
非常感谢@innoSPG关于缓存内存(和缓存故障)的答案,这些内容非常丰富且有用!
澄清
最后,从评论中,我意识到这个问题的重点实际上是关于过程调用,而不是与openmp并行化严格相关。也就是说,我已经离开了openmp语句,因为这是我更复杂的应用程序中发生的事情,我希望尽可能保留它。从@ Chaosit的评论看来,程序调用将需要开销,减慢方法3.有没有办法解决这个问题?
另外,如果我错了,请纠正我,但我相信方法2中的两个操作将按书面顺序执行,从而产生正确的最终x
值。
答案 0 :(得分:4)
我的理解是,您正在寻找的差异(方法2和方法3)不依赖于并行化。
让我解释一下:
方法3可能会因为方法2中不存在的过程调用而产生另一个开销。我说可能是因为我不熟悉过程指针,但是,我的猜测是编译器的优化不会内联如果使用过程指针,则调用过程。
由于过程调用而产生的开销完全独立于并行化。您仍将在顺序代码中使用它。它不是并行化延迟的一部分。
现在,我冒着风险回到你不喜欢的选项:试试看。我冒这个风险是因为我相信你所展示的只是你问题的一个例子,你的实际案例更复杂。当您遇到复杂的情况时,您可能会惊讶于方法1和方法2之间的结论不再有效。它可能在很大程度上取决于您手头的问题。一个案件的结论不适用于另一个案件。像现在的编译器一样聪明,他们不会抓住一切。在计算机科学中,有一种高速缓冲存储器的概念有时候还不太清楚,但我不会详细介绍。您可以将方法1的每个单个循环(数据和/或代码)保存在高速缓存存储器中,而情况2的组合循环不保存在高速缓冲存储器中。简单说明是方法2在内循环内经历高速缓存故障而方法1不经历高速缓存故障的情况。在这种情况下,您将看到方法1高于大循环边界的方法2。
这就是Try and see
成为标准的地方,这就是现实案例中大部分时间都会发生的事情。