Fortran/Python/MATLAB之间MKL矩阵乘法性能的奇异差异

4
我编写了一个简单的基准测试,比较了三种语言中矩阵乘法的性能 - Fortran(使用Intel Parallel Studio 2015,编译时使用ifort开关:/O3 /Qopt-prefetch=2 /Qopt-matmul /Qmkl:parallel,这将MatMul调用替换为对Intel MKL库的调用),Python(使用当前Anaconda版本,包括Anaconda Accelerate,提供与Intel MKL库链接的NumPy 1.9.2)和MATLAB R2015a(再次使用Intel MKL库进行矩阵乘法)。
鉴于这三个实现都利用相同的Intel MKL库进行矩阵乘法,我期望结果在足够大的矩阵下,函数调用开销可以忽略不计时是几乎相同的。然而,情况远非如此,虽然MATLAB和Python展示了几乎相同的性能,但Fortran击败了两者2-3倍。我想了解其中的原因。
以下是我用于Fortran版本的代码:
program MatMulTest

implicit none

integer, parameter :: N = 1024
integer :: i, j, cr, cm
real*8 :: t0, t1, rate
real*8 :: A(N,N), B(N,N), C(N,N)    

call random_seed()
call random_number(A)
call random_number(B)

! First initialize the system_clock
CALL system_clock(count_rate=cr)
CALL system_clock(count_max=cm)
rate = real(cr)
WRITE(*,*) "system_clock rate: ", rate

call cpu_time(t0)
do i = 1, 100, 1
    C=MatMul(A,B)                
end do
call cpu_time(t1)

write(unit=*, fmt="(a24,f10.5,a2)") "Average time spent: ", (t1-t0), "ms"
write(unit=*, fmt="(a24,f10.3)") "First element of C: ", C(1,1)

end program MatMulTest

请注意,如果您的系统时钟速率不是像我这样的10000,您需要相应地修改计时计算以产生毫秒。
Python代码:
import time
import numpy as np

def main(N):
    A = np.random.rand(N,N)
    B = np.random.rand(N,N)
    for i in range(100):
        C = np.dot(A,B)
    print C[0,0]

if __name__ == "__main__":
    N = 1024
    t0 = time.clock()
    main(N)
    t1 = time.clock()
    print "Time elapsed: " + str((t1-t0)*10) + " ms"

最后,这是MATLAB代码片段:

N=1024;
A=rand(N,N); B=rand(N,N);
tic;
for i=1:100
     C=A*B;
end
t=toc;
disp(['Time elapsed: ', num2str(t*10), ' milliseconds'])

在我的系统上,结果如下:
Fortran: 38.08 ms
Python: 104.29 ms
MATLAB: 97.36 ms

CPU使用率在所有三种情况下均无法区分(在启用超线程的i7-920D0处理器上,使用稳定的47-49%进行计算)。此外,除了对于非常小的矩阵(N<80左右)需要手动禁用Fortran中的并行化之外,任意矩阵大小的相对性能保持大致相等。这里是否有任何已知的原因导致差异?我做错了什么吗?我希望至少对于更大的矩阵,Fortran在这种情况下没有实质上的优势。

2
如注释所述:不要计时for循环,只需计时乘法! - Ander Biguri
为什么你要询问 system_clock() 的速率,然后又用 cpu_time() 来测量时间呢?使用其中一个就可以了,它们有不同的目的。 - Vladimir F Героям слава
2
其中一个原因,尽管看起来很愚蠢,可能是for循环。在Python/Matlab中,循环通常比C/Fortran慢。正如@AnderBiguri建议的那样,尝试仅计时矩阵乘法,在3种语言中选择100次计时并取平均值。 - Imanol Luengo
1个回答

6
你有两个问题:
  1. 在Python中,你计时了随机初始化和计算,而在Fortran和MATLAB中没有。
  2. 在Fortran中,你测量CPU时间,而在Python和MATLAB中测量经过的时间。由于你注意到CPU使用率约为46%,这可能导致差异。
只需解决这两个问题并重试...你可以考虑使用 date_and_time() 而不是 cpu_time() 来实现这个目的。

1
很遗憾地说,这两个建议没有任何可衡量的区别。Python现在可以比之前更可靠地匹配MATLAB,但使用date_and_time()函数的Fortran计时与以前完全相同,总体差异仍然存在。 - user13492
2
除了date_and_time(),我发现使用system_clock()更容易进行计时。 - Vladimir F Героям слава
2
@VladimirF 的确,system_clock() 可能更简单易用。我自己倾向于尽可能使用 omp_get_wtime() - Gilles
1
你尝试将 KMP_NUM_THREADS 设置为1,只是为了看看不同的语言是否会导致并行化策略上的差异吗? - Gilles
2
在使用system_clock()计时结果并考虑并行化差异后,只要使用合理大小的矩阵,我现在得到了几乎相同的三种语言的结果。我还转而仅计时乘法而不是循环,但这并没有显著影响结果。感谢所有有用的评论,我将接受所提议的答案,因为它总体上是最全面的,并附有相关评论。 - user13492
MATLAB和R不是同一件事情! - IKavanagh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接