optimization of a seven do cycle

algorithm, fortran, fortran90, math, performance

Solution

The trick in these things is to look for common patterns and use existing efficient routines to speed them up.

M.S.B is, as usual, completely right that just flipping your indices will give you substantial speedup, although intel's fortran compiler with high optimization will already give you some of that benefit.

But let's peel off the `m` index for a second (which is easy to do as, as MSB has pointed out, that's the slowest-moving index) and just look at the multiplication:

Ai,j,k,l = ∑ Bk,l,r,s × Pi,j,r,s Ai,j,k,l = ∑ Pi,j,r,s × Bk,l,r,s

reshaping the arrays:

Aij,kl = ∑ Pij,rs × Bkl,rs Aij,kl = ∑ Pij,rs × BTrs,kl A = P × BT

where we now have matrix multiplication, for which very efficient routines exist. So if we reshape the P and B matrices, and transpose B, we can do a simple matrix multiplication and reshape the result; and this reshape won't even necessarily require any copies in this case. So changing something like this:

program testpsum
implicit none

integer, dimension(10,10,10,10,10) :: B
integer, dimension(32,32,10,10,10) :: P
integer, dimension(32,32,10,10,10) :: A
integer :: psum
integer :: i, j, k, l, m, r, s

B = 1
P = 2

do i=1,32
  do j=1,32
    do k=1,10
     do l=1,10
      do m=1,10
       do r=1,10
        do s=1,10
          psum=psum+B(k,l,r,s,m)*P(i,j,r,s,m)
        end do
       end do
       A(i,j,k,l,m)=psum
       psum = 0
     end do
    end do
   end do
 end do
end do

print *,minval(A), maxval(A)

end program testpsum

To this:

program testmatmult
implicit none

integer, dimension(10,10,10,10,10) :: B
integer, dimension(32,32,10,10,10) :: P
integer, dimension(10*10,10*10) :: Bmt
integer, dimension(32*32,10*10) :: Pm
integer, dimension(32,32,10,10,10) :: A
integer :: m

B = 1
P = 2

do m=1,10
    Pm  = reshape(P(:,:,:,:,m),[32*32,10*10])
    Bmt = transpose(reshape(B(:,:,:,:,m),[10*10,10*10]))
    A(:,:,:,:,m) = reshape(matmul(Pm,Bmt),[32,32,10,10])
end do

print *,minval(A), maxval(A)

end program testmatmult

Gives timings of:

$ time ./psum
         200         200

real    0m2.239s
user    0m1.197s
sys 0m0.008s

$ time ./matmult
         200         200

real    0m0.064s
user    0m0.027s
sys 0m0.008s

when compiled with `ifort -O3 -xhost -mkl` so we can use the fast intel MKL libraries. It gets even faster when you don't create that `Pm` temporary and just do the reshape in the matmult call, and faster still (for large matrices) if you use `-mkl=parallel` for threaded routines. If you don't also have MKL you can just link to some other fast LAPACK _GEMM routine.

Problem

I have 3 array and I have to do this summation The implemented code is ``` do i=1,320 do j=1,320 do k=1,10 do l=1,10 do m=1,10 do r=1,10 do s=1,10 sum=sum+B(k,l,r,s,m)*P(i,j,r,s,m) end do end do A(i,j,k,l,m)=sum end do end do end do end do end do ``` It takes 1 day to execute the code. Is there a way to optimize it? Thanks.

Original source