【问题标题】:How to speed up simple Fortran OpenMP?如何加速简单的 Fortran OpenMP?
【发布时间】:2013-05-08 19:12:58
【问题描述】:

我有一个简单的 Fortran 程序,其中主要组件是计算点积的 4 核 OpenMP 部分

OMP_NUM_THREADS=4
...
Do 30 k=1,lines
  co(k)=0
  si(k)=0
  co_temp=0
  si_temp=0

!$OMP PARALLEL DO PRIVATE(dotprod,Qcur) REDUCTION(+:co_temp,si_temp)
    Do 40 i=1,ION_COUNT
      dotprod=(rx(k)*x(i)+ry(k)*y(i)+rz(k)*z(i))*((2*3.1415926535)/l)
      co_temp=co_temp+COS(dotprod)*26 !Qcur/Qavg
      si_temp=si_temp+SIN(dotprod)*26 !Qcur/Qavg
     40 continue

!$OMP END PARALLEL DO

  co(k)=co_temp
  si(k)=si_temp

  q(k)= ( co(k),-si(k) )
  s(k)= s(k) +( q(k) * conjg(q(k)) )
  r(k)=r(k)+q(k)
30 continue

我对 Fortran 或其优化不是很有经验。我正在使用 xlf90_r 文件 -qsmp=omp 编译。使用 4 核时我只获得了大约 1/2 的加速,使用 C 的其他人在执行相同的计算时获得了几乎完美的 1/4 加速。无论 OMP 循环是在 30 还是 40 上,我得到的时间都差不多。此外,我围绕循环 30 以及整个程序进行计时,这个循环占用了 99.x% 的时间,所以我很确定这一点是瓶颈。我在这部分中犯了任何令人震惊的缓慢错误,任何人都可以看到?

【问题讨论】:

  • 这个问题的表述有点模糊。你能生成一个重现行为的小型可编译测试(FORTRAN + C)吗?您的目标是哪台 IBM 计算机?只是为了乏味,1/2 加速是 2x 减速:-)
  • 这种方法的问题是我不会C,否则我会。 IBM计算机是超级计算机上的4核节点。 PowerPC 970 MP 是页面说它使用的核心。本质上,我只是想知道是否有人看到任何会显着减慢速度的东西。
  • C 代码是否在同一台机器上运行?
  • 如果您真的在使用超级计算机,我建议您将问题重定向到用户支持团队。如果没有用户支持团队,那么您就不是在使用超级计算机!
  • C 代码在同一台机器和节点上运行,或者在同一节点上运行。用户支持团队可能不太关心我的问题;可以说我不是他们的第一要务(或第二或第三......)。我正在查看大约 5.5 小时的总迭代时间,内循环约 28,000 次循环,外循环约 650 万次。

标签: fortran openmp


【解决方案1】:

快速浏览一下您的代码,您的外部循环的每次迭代似乎都是独立的。我会让并行循环而不是内部循环。

OMP_NUM_THREADS=4
...
!$OMP PARALLEL DO PRIVATE(dotprod,Qcur,co_temp,si_temp)
Do 30 k=1,lines
  co(k)=0
  si(k)=0
  co_temp=0
  si_temp=0

  Do 40 i=1,ION_COUNT
    dotprod=(rx(k)*x(i)+ry(k)*y(i)+rz(k)*z(i))*((2*3.1415926535)/l)
    co_temp=co_temp+COS(dotprod)*26 !Qcur/Qavg
    si_temp=si_temp+SIN(dotprod)*26 !Qcur/Qavg
  40 continue

  co(k)=co_temp
  si(k)=si_temp

  q(k)= ( co(k),-si(k) )
  s(k)= s(k) +( q(k) * conjg(q(k)) )
  r(k)=r(k)+q(k)
30 continue
!$OMP END PARALLEL DO

【讨论】:

    【解决方案2】:

    很可能,使用 C 进行的测试是在更好的处理器中执行的,并且您使用的是双核。如果属实,我预计不会有比 2 更好的加速。

    正如@user1139069 所建议的,您应该在 k 上并行化第一个循环,以避免浪费 i 倍于创建线程组所需的运行时间。

    我还认为您可能有错误共享问题,因为您的线程可能在您的数组的 neibourgh 元素上工作。为避免这种情况,我建议将循环 40 替换为类似

       Do 40 ii=1,ION_COUNT/nCacheSize
         DO 41 i_leap=1,nCacheSize
           i=(ii-1)*nCacheSize+i_leap 
           ...
         41 CONTINUE
       40 CONTINUE
    

    这样可以强制线程在不同的内存缓存行上工作。我认为这将加快您的代码速度。

    请注意,nCacheSize 应该通过parameter(nCacheSize=8) 声明为常量。您应该输入的数字取决于您的机器和变量类型。所以尝试使用 2、4、8、16、32 来找到最佳值。

    【讨论】:

      【解决方案3】:

      由于我不太明白的原因,将 OMP 放在外循环上会(非常轻微地)慢一些。我无法弄清楚为什么它不能完美地并行化。但是,我能够明显加快这段代码的速度。我将 2*l*pi 变量更改为单个变量,并且只有 8 位数字。我还删除了 *26,因为我可以简单地将最终值乘以 26 或 26^2。我得到了大约 30% 的加速。不会猜到,但你去。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-04-01
        • 1970-01-01
        • 2018-06-17
        • 2013-04-22
        • 1970-01-01
        • 2011-11-02
        相关资源
        最近更新 更多