Fortran/Python/MATLAB 之间 MKL 矩阵乘法性能的特殊差异
Peculiar difference in MKL matrix multiplication performance between Fortran/Python/MATLAB
我用三种语言编写了一个比较矩阵乘法性能的简单基准测试 - Fortran(使用 Intel Parallel Studio 2015,使用 ifort 开关编译:/O3 /Qopt-prefetch=2 /Qopt-matmul /Qmkl:parallel ,这用对英特尔 MKL 库的调用替换了 MatMul 调用),Python(使用当前的 Anaconda 版本,包括 Anaconda Accelerate,它提供与英特尔 MKL 库链接的 NumPy 1.9.2)和 MATLAB R2015a(再次,使用英特尔 MKL 库进行矩阵乘法)。
看到这三个实现如何使用相同的英特尔 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 使用在所有三种情况下都无法区分(在计算期间启用 HT 的 i7-920D0 处理器上使用稳定的 47-49%)。此外,对于任意大小的矩阵,相对性能大致相同,但对于非常小的矩阵(N<80 左右),在 Fortran 中手动禁用并行化是有用的。
这里的差异是否有确定的原因?难道我做错了什么?我希望至少对于较大的矩阵,Fortran 在这种情况下不会有任何有意义的优势。
这里有两个问题:
- 在 Python 中,您为随机初始化和计算计时,这在 Fortran 和 MATLAB 中没有
- 在 Fortran 中,您测量 CPU 时间,而在 Python 和 MATLAB 中测量经过的时间。由于您注意到 CPU 使用率约为 46%,这可能只是造成差异的原因。
只需解决这两个问题,然后重试...为此,您可以考虑使用 date_and_time()
而不是 cpu_time()
。
我用三种语言编写了一个比较矩阵乘法性能的简单基准测试 - Fortran(使用 Intel Parallel Studio 2015,使用 ifort 开关编译:/O3 /Qopt-prefetch=2 /Qopt-matmul /Qmkl:parallel ,这用对英特尔 MKL 库的调用替换了 MatMul 调用),Python(使用当前的 Anaconda 版本,包括 Anaconda Accelerate,它提供与英特尔 MKL 库链接的 NumPy 1.9.2)和 MATLAB R2015a(再次,使用英特尔 MKL 库进行矩阵乘法)。
看到这三个实现如何使用相同的英特尔 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 使用在所有三种情况下都无法区分(在计算期间启用 HT 的 i7-920D0 处理器上使用稳定的 47-49%)。此外,对于任意大小的矩阵,相对性能大致相同,但对于非常小的矩阵(N<80 左右),在 Fortran 中手动禁用并行化是有用的。
这里的差异是否有确定的原因?难道我做错了什么?我希望至少对于较大的矩阵,Fortran 在这种情况下不会有任何有意义的优势。
这里有两个问题:
- 在 Python 中,您为随机初始化和计算计时,这在 Fortran 和 MATLAB 中没有
- 在 Fortran 中,您测量 CPU 时间,而在 Python 和 MATLAB 中测量经过的时间。由于您注意到 CPU 使用率约为 46%,这可能只是造成差异的原因。
只需解决这两个问题,然后重试...为此,您可以考虑使用 date_and_time()
而不是 cpu_time()
。