优化循环:巨大的数组操作

时间:2015-11-17 10:46:16

标签: performance matrix fortran simd hpc

我正在对不适合缓存的数组进行大量计算(这里的衍生物,但看起来类似于图像操作),这意味着CPU必须在缓存中加载部件,计算,然后加载另一部件等等。但是因为对于计算的形状,一些数据是多次加载,卸载和重新加载。我想知道是否有办法优化这一点。我已经使用编译器优化(GCC和Intel)使用SIMD指令。

这是Fortran计算,但它类似于C / C ++,内存顺序只是反转而数组使用()而不是[]for替换为do

在x ax上:

   do k=1,N(3)
      do j=1,N(2)
         do i=3,N(1)
            DF(i,j,k)=(F(i+1,j,k)-F(i-1,j,k))*B+(F(i-2,j,k)-F(i+2,j,k))*C
         end do
      end do
   end do

on y ax:

   do k=1,N(3)
      do j=1,N(2)
         do i=3,N(1)
            DF(i,j,k)=(F(i,j+1,k)-F(i,j-1,k))*B+(F(i,j-2,k)-F(i,j+2,k))*C
         end do
      end do
   end do

on z ax:

   do k=1,N(3)
      do j=1,N(2)
         do i=3,N(1)
            DF(i,j,k)=(F(i,j,k+1)-F(i,j,k-1))*B+(F(i,j,k-2)-F(i,j,k+2))*C
         end do
      end do
   end do

ax x上的一阶导数是正常的,因为连续读取内存。 y轴和z轴上的导数不连续。

最糟糕的计算是我将所有轴组合在一起(这是拉普拉斯算子):

 do k=1,N(3)
    do j=1,N(2)
       do i=3,N(1)
          V(i,j,k) = M(i,j,k,1) * p(i,j,k) &
                 & + M(i,j,k,2) * p(i-1,j,k) &
                 & + M(i,j,k,3) * p(i+1,j,k) &
                 & + M(i,j,k,4) * p(i,j-1,k) &
                 & + M(i,j,k,5) * p(i,j+1,k) &
                 & + M(i,j,k,6) * p(i,j,k-1) &
                 & + M(i,j,k,7) * p(i,j,k+1)
       end do
    end do
 end do

请注意,编译器不理解上一次操作(拉普拉斯算子)。要使用SIMD(矢量化计算),我需要像这样拆分操作,这样可以加速2.5倍:

 do k=1,N(3)
    do j=1,N(2)
       do i=3,N(1)
          V(i,j,k) = M(i,j,k,1) * p(i,j,k) &
                 & + M(i,j,k,2) * p(i-1,j,k) &
                 & + M(i,j,k,3) * p(i+1,j,k)
       end do
    end do
 end do
 do k=1,N(3)
    do j=1,N(2)
       do i=3,N(1)
          V(i,j,k) = V(i,j,k) + &
                 & + M(i,j,k,4) * p(i,j-1,k) &
                 & + M(i,j,k,5) * p(i,j+1,k)
       end do
    end do
 end do
 do k=1,N(3)
    do j=1,N(2)
       do i=3,N(1)
          V(i,j,k) = V(i,j,k) + &
                 & + M(i,j,k,6) * p(i,j,k-1) &
                 & + M(i,j,k,7) * p(i,j,k+1)
       end do
    end do
 end do

也许使用SIMD我已达到最高速度,但由于这些计算需要数天,即使使用MPI和超过1024 CPU,减少计算时间,即使是20%也是一个很好的步骤! 你们中有谁有关于如何优化这个的想法吗?

1 个答案:

答案 0 :(得分:3)

当您使用3D模具并引用i,j,k-1i,j,k+1等元素时,您通过数组的线性顺序将不是最佳的。缓存效率可以提高loop tiling

在我的代码中我使用

!$omp parallel private(i,j,k,bi,bj,bk)
!$omp do schedule(runtime) collapse(3)
do bk = 1, Unz, tnz
 do bj = 1, Uny, tny
  do bi = 1, Unx, tnx
   do k = bk, min(bk+tnz-1,Unz)
    do j = bj, min(bj+tny-1,Uny)
     do i = bi, min(bi+tnx-1,Unx)
         U2 (i,j,k) = U2(i,j,k) + &
           (U(i+1,j,k)-U(i,j,k)) * ...
         U2(i,j,k) = U2(i,j,k) - &
            (U(i,j,k)-U(i-1,j,k)) * ...
         U2(i,j,k) = U2(i,j,k) + &
            (U(i,j+1,k)-U(i,j,k)) * ...
         U2(i,j,k) = U2(i,j,k) - &
            (U(i,j,k)-U(i,j-1,k)) * ...
         U2(i,j,k) = U2(i,j,k) + &
            (U(i,j,k+1)-U(i,j,k)) * ...
         U2(i,j,k) = U2(i,j,k) - &
            (U(i,j,k)-U(i,j,k-1)) * ...
     end do
    end do
   end do
  end do
 end do
end do
!$omp end do

其中tnxtnytnz是您在i,j,k顺序中迭代的磁贴的大小。必须将大小设置为接近L1高速缓存。这将增加重新加载加载到缓存中的内容。

如果您需要分开指示,您当然可以这样做并仍然保留平铺。