我编写了一个简单的基准测试,比较了三种语言中矩阵乘法的性能 - 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版本的代码:
请注意,如果您的系统时钟速率不是像我这样的10000,您需要相应地修改计时计算以产生毫秒。
Python代码:
在我的系统上,结果如下:
CPU使用率在所有三种情况下均无法区分(在启用超线程的i7-920D0处理器上,使用稳定的47-49%进行计算)。此外,除了对于非常小的矩阵(N<80左右)需要手动禁用Fortran中的并行化之外,任意矩阵大小的相对性能保持大致相等。这里是否有任何已知的原因导致差异?我做错了什么吗?我希望至少对于更大的矩阵,Fortran在这种情况下没有实质上的优势。
鉴于这三个实现都利用相同的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在这种情况下没有实质上的优势。
system_clock()
的速率,然后又用cpu_time()
来测量时间呢?使用其中一个就可以了,它们有不同的目的。 - Vladimir F Героям славаfor
循环。在Python/Matlab中,循环通常比C/Fortran慢。正如@AnderBiguri建议的那样,尝试仅计时矩阵乘法,在3种语言中选择100次计时并取平均值。 - Imanol Luengo