OPENMP F90/95 嵌套 DO 循环 - 与串行实现相比得到改进的问题

Posted

技术标签:

【中文标题】OPENMP F90/95 嵌套 DO 循环 - 与串行实现相比得到改进的问题【英文标题】:OPENMP F90/95 Nested DO loops - problems getting improvement over serial implementation 【发布时间】:2011-06-06 20:50:37 【问题描述】:

我进行了一些搜索,但找不到任何似乎与我的问题相关的内容(对不起,如果我的问题是多余的!)。无论如何,正如标题所述,我无法对我的代码的串行实现进行任何改进。我需要并行化的代码 sn-p 如下(这是 Fortran90 with OpenMP):

do n=1,lm     
  do m=1,jm   
    do l=1,im      
      sum_u = 0
      sum_v = 0
      sum_t = 0
      do k=1,lm
       !$omp parallel do reduction (+:sum_u,sum_v,sum_t) 
        do j=1,jm  
          do i=1,im
            exp_smoother=exp(-(abs(i-l)/hzscl)-(abs(j-m)/hzscl)-(abs(k-n)/vscl))
            sum_u = sum_u + u_p(i,j,k) * exp_smoother
            sum_v = sum_v + v_p(i,j,k) * exp_smoother
            sum_t = sum_t + t_p(i,j,k) * exp_smoother

            sum_u_pert(l,m,n) = sum_u
            sum_v_pert(l,m,n) = sum_v
            sum_t_pert(l,m,n) = sum_t          

            end do
          end do
       end do      
    end do
  end do  
end do

我是否遇到了竞争条件问题?还是我只是将指令放在错误的位置?我对此很陌生,所以如果这是一个过于简单化的问题,我深表歉意。

无论如何,如果没有并行化,代码会非常缓慢。为了了解问题的大小,lm、jm 和 im 索引分别为 60、401 和 501。所以并行化是至关重要的。非常感谢任何帮助或有用资源的链接!我正在使用 xlf 来编译上面的代码,如果这有用的话。

谢谢! -珍

【问题讨论】:

【参考方案1】:

放置 omp pragma 的明显位置是在非常外部的循环中。

对于每个 (l,m,n),您都在计算扰动变量和指数平滑器之间的卷积。每个 (l,m,n) 计算完全独立于其他计算,因此您可以将其放在最外层循环中。所以例如最简单的事情

!$omp parallel do private(n,m,l,i,j,k,exp_smoother) shared(sum_u_pert,sum_v_pert,sum_t_pert,u_p,v_p,t_p), default(none)
do n=1,lm
  do m=1,jm
    do l=1,im
      do k=1,lm
        do j=1,jm
          do i=1,im
            exp_smoother=exp(-(abs(i-l)/hzscl)-(abs(j-m)/hzscl)-(abs(k-n)/vscl))
            sum_u_pert(l,m,n) = sum_u_pert(l,m,n) + u_p(i,j,k) * exp_smoother
            sum_v_pert(l,m,n) = sum_v_pert(l,m,n) + v_p(i,j,k) * exp_smoother
            sum_t_pert(l,m,n) = sum_t_pert(l,m,n) + t_p(i,j,k) * exp_smoother
          end do
        end do
      end do
    end do
  end do
end do

在 8 核上给了我约 6 倍的加速(使用大大减小的 20x41x41 的问题大小)。考虑到循环中要做的工作量,即使在较小的尺寸下,我认为它不是 8 倍加速的原因涉及内存争用或错误共享;为了进一步调整性能,您可能希望将 sum 数组显式分解为每个线程的子块,并在最后组合它们;但根据问题的大小,可能不希望有一个额外的 im x jm x lm 大小的数组。

在这个问题中似乎有很多结构,即使是串行案例,您也可以对其进行探索以加快速度,但是说起来更容易找到它;在纸和笔上玩耍几分钟后什么都不会想到,但更聪明的人可能会发现一些东西。

【讨论】:

谢谢!这很有帮助。我现在正在尝试一下。不幸的是,我仍然有办法达到我需要的性能水平。我可以使用一台相当大的机器(每个处理器 64 个内核)——但也许我可能需要将它与 MPI 结合起来以获得必要的性能。 这里 MPI 的缺点可能是一些数组的额外副本,但是计算是如此独立,如果跨 MPI 的分解对您的问题的其余部分有意义,那么它应该很漂亮简单的。 xlf + 64 核 == 电源系统?确保您使用的是 -O5、-qhot 和适当的 -qarch 标志,并让编译器真正使用这个标志。有 64 个内核,最外层循环只有大约 60 次迭代,您可能会发现在 omp do 行中添加一个 collapse(2) 或 collapse(3) 子句是有利的。 使用 Power 系统,您的钱是对的。除了编译器标志之外,我明天还要试一试collapse 子句。我也意识到没有必要在整个网格上进行计算(对于我的问题)——所以这也应该会大大加快速度。再次感谢!我不知道堆栈溢出是一个很棒的资源!【参考方案2】:

你所拥有的是卷积。这可以通过 N log2(N) 时间内的快速傅里叶变换来完成。你的算法是 N^2。如果你使用 FFT,一个核可能就足够了!

【讨论】:

以上是关于OPENMP F90/95 嵌套 DO 循环 - 与串行实现相比得到改进的问题的主要内容,如果未能解决你的问题,请参考以下文章

OpenMP 如何处理嵌套循环?

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

OpenMP 矩阵乘法嵌套循环

嵌套循环的 OpenMP SIMD 矢量化

OpenMP 嵌套循环任务并行性,计数器未给出正确结果

用于嵌套 for 循环的 OpenMP?