大多数编译器是否优化MATMUL(TRANSPOSE(A),B)?

G. *_*ron 5 fortran gfortran micro-optimization intel-fortran

在Fortran程序中,我需要计算几个表达式,例如M · vM T · vM T · MM · M T等。在这里,Mv是小尺寸(较小的2D和1D数组)大于100,通常为2-10)

我想知道MATMUL(TRANSPOSE(M),v)在编译时编写的代码是否可以像MATMUL(N,v)N显式存储为的代码中一样高效地展开N=TRANSPOSE(M)。我对带有“强”优化标志(例如-O2,-O3或-Ofast)的gnu和ifort编译器特别感兴趣。

kva*_*our 5

在下面,您可以找到各种方法的执行时间。

系统:

  • 英特尔(R)酷睿TM i5-6500T CPU @ 2.50GHz
  • 缓存大小:6144 KB
  • 内存:16MB
  • GNU Fortran(GCC)6.3.1 20170216(Red Hat 6.3.1-3)
  • 伊福特(IFORT)18.0.5 20180823
  • BLAS:对于gnu编译器,使用的blas是默认版本

汇编:

[gnu] $ gfortran -O3 x.f90 -lblas
[intel] $ ifort -O3 -mkl x.f90
Run Code Online (Sandbox Code Playgroud)

执行:

[gnu] $ ./a.out > matmul.gnu.txt
[intel] $ EXPORT MKL_NUM_THREADS=1; ./a.out > matmul.intel.txt
Run Code Online (Sandbox Code Playgroud)

为了使结果尽可能中性,我用一组等效操作的平均时间重新调整了答案。我忽略了线程。

矩阵时间向量

比较了六个不同的实现:

  1. 手册: do j=1,n; do k=1,n; w(j) = P(j,k)*v(k); end do; end do
  2. matmul: matmul(P,v)
  3. blas N:dgemv('N',n,n,1.0D0,P,n,v,1,0,w,1)
  4. matmul-transpose: matmul(transpose(P),v)
  5. matmul-transpose-tmp: Q=transpose(P); w=matmul(Q,v)
  6. 爆破: dgemv('T',n,n,1.0D0,P,n,v,1,0,w,1)

在图1和图2中,您可以比较上述情况的时序结果。整体而言,我们可以说,一个临时的用法是在双方gfortranifort不明智的。两种编译器都能优化MATMUL(TRANSPOSE(P),v)得更好。在中gfortran,的实现MATMUL比默认的BLAS更快,ifort清楚地表明它mkl-blas更快。

在此处输入图片说明 图1:矩阵向量乘法。比较了各种实现gfortran。左面板显示绝对时间除以手动计算的总时间(对于大小为1000的系统)。右面板显示绝对时间除以n2×?。这里 ?是大小为1000的手动计算的平均时间除以1000×1000。

在此处输入图片说明 图2:矩阵向量乘法。各种实现的比较是在单线程ifort编译上进行的。左面板显示绝对时间除以手动计算的总时间(对于大小为1000的系统)。右面板显示绝对时间除以n2×?。这里 ?是大小为1000的手动计算的平均时间除以1000×1000。

矩阵乘以矩阵

比较了六个不同的实现:

  1. 手册: do l=1,n; do j=1,n; do k=1,n; Q(j,l) = P(j,k)*P(k,l); end do; end do; end do
  2. matmul: matmul(P,P)
  3. blas N:dgemm('N','N',n,n,n,1.0D0,P,n,P,n,0.0D0,R,n)
  4. matmul-transpose: matmul(transpose(P),P)
  5. matmul-transpose-tmp: Q=transpose(P); matmul(Q,P)
  6. 爆破: dgemm('T','N',n,n,n,1.0D0,P,n,P,n,0.0D0,R,n)

在图3和图4中,您可以比较上述情况的时序结果。与向量情况相反,仅建议将临时情况用于gfortran。在中gfortran,的实现MATMUL比默认的BLAS更快,ifort清楚地表明它mkl-blas更快。值得注意的是,手动实施与相当mkl-blas

在此处输入图片说明 图3:矩阵-矩阵乘法。比较了各种实现gfortran。左面板显示绝对时间除以手动计算的总时间(对于大小为1000的系统)。右面板显示绝对时间除以n3×?。这里 ?是大小为1000的手动计算的平均时间除以1000×1000×1000的时间。

在此处输入图片说明 图4:矩阵-矩阵乘法。各种实现的比较是在单线程ifort编译上进行的。左面板显示绝对时间除以手动计算的总时间(对于大小为1000的系统)。右面板显示绝对时间除以n3×?。这里 ?是大小为1000的手动计算的平均时间除以1000×1000×1000的时间。


使用的代码:

program matmul_test

  implicit none

  double precision, dimension(:,:), allocatable :: P,Q,R
  double precision, dimension(:), allocatable :: v,w

  integer :: n,i,j,k,l
  double precision,dimension(12) :: t1,t2

  do n = 1,1000
     allocate(P(n,n),Q(n,n), R(n,n), v(n),w(n))
     call random_number(P)
     call random_number(v)

     i=0

     i=i+1
     call cpu_time(t1(i))
     do j=1,n; do k=1,n; w(j) = P(j,k)*v(k); end do; end do
     call cpu_time(t2(i))

     i=i+1
     call cpu_time(t1(i))
     w=matmul(P,v)
     call cpu_time(t2(i))

     i=i+1
     call cpu_time(t1(i))
     call dgemv('N',n,n,1.0D0,P,n,v,1,0,w,1)
     call cpu_time(t2(i))

     i=i+1
     call cpu_time(t1(i))
     w=matmul(transpose(P),v)
     call cpu_time(t2(i))

     i=i+1
     call cpu_time(t1(i))
     Q=transpose(P)
     w=matmul(Q,v)
     call cpu_time(t2(i))

     i=i+1
     call cpu_time(t1(i))
     call dgemv('T',n,n,1.0D0,P,n,v,1,0,w,1)
     call cpu_time(t2(i))

     i=i+1
     call cpu_time(t1(i))
     do l=1,n; do j=1,n; do k=1,n; Q(j,l) = P(j,k)*P(k,l); end do; end do; end do
     call cpu_time(t2(i))

     i=i+1
     call cpu_time(t1(i))
     Q=matmul(P,P)
     call cpu_time(t2(i))

     i=i+1
     call cpu_time(t1(i))
     call dgemm('N','N',n,n,n,1.0D0,P,n,P,n,0.0D0,R,n)
     call cpu_time(t2(i))

     i=i+1
     call cpu_time(t1(i))
     Q=matmul(transpose(P),P)
     call cpu_time(t2(i))

     i=i+1
     call cpu_time(t1(i))
     Q=transpose(P)
     R=matmul(Q,P)
     call cpu_time(t2(i))

     i=i+1
     call cpu_time(t1(i))
     call dgemm('T','N',n,n,n,1.0D0,P,n,P,n,0.0D0,R,n)
     call cpu_time(t2(i))

     write(*,'(I6,12D25.17)') n, t2-t1
     deallocate(P,Q,R,v,w)
  end do

end program matmul_test
Run Code Online (Sandbox Code Playgroud)