如何加速简单的Fortran OpenMP?

时间:2013-05-08 19:12:58

标签: fortran openmp

我有一个简单的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%的时间,所以我很确定这一点是瓶颈。我在这个部分做过的任何令人震惊的缓慢错误,任何人都看到了吗?

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

可能是在更好的处理器上执行了C测试,并且您正在使用双核。如果为true,则我预计不会有比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

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

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

答案 2 :(得分:-1)

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