我尝试使用 f2py、Cython 和 Numba 使简单的循环结构在 python 中更快。Python实现:
def Python(A):
B = 0.0
for i in range(100):
for j in range(100):
for k in range(100):
for l in range(100):
B = B + A[i,j,k,l]
return B
Numa 实现:
@jit(double(double [:,:,:,:]), nopython=True)
def Numba(A):
B = 0.0
for i in range(100):
for j in range(100):
for k in range(100):
for l in range(100):
B = B + A[i,j,k,l]
return B
Cython 实现:
cdef double Cython(double [:,:,:,:] A):
cdef double B
cdef int i, j, k, l
B = 0.0
for i in range(0,100):
for j in range(0,100):
for k in range(0,100):
for l in range(0,100):
B = B + A[i,j,k,l]
return B
f2py 实现:
SUBROUTINE F2PY_run(A,B)
INTEGER :: i, j, k, l
REAL(8), DIMENSION(100,100,100,100), INTENT(in) :: A
REAL(8), INTENT(out) :: B
B = 0.0d0
DO i=1, 100
DO j=1, 100
DO k=1, 100
DO l=1,100
B = B + A(l,k,j,i)
END DO
END DO
END DO
END DO
END SUBROUTINE F2PY_run
Cython 编译:
setup(ext_modules=cythonize(Extension('runtest',['runtest.pyx'])), include_dirs=[numpy.get_include()])
f2py 编译:
f2py -c runFortran.F90 --f90flags=-O3 -m fortranrun
这些函数是通过将 np.random.rnd(100,100,100,100) 传递给函数来运行的。时间安排如下:
22.998 sec; Python
0.114 sec; Cython
2.678 sec; f2py
0.106 sec; numba
我知道对于我设置的这个特定问题,我可以使用 np.einsum()。np.einsum() 的时间为 0.061 秒。但是对于我的“真实”代码,einsum 无法解决这个问题。可以看出 Cython 和 Numba 的执行速度大致相同,而 f2py 则慢得多。f2py 函数是用 Fortran90 编写的,所以我原以为它至少会这么快。因此,我的问题是,我是否错误地使用了 f2py?还是 f2py 有速度问题?
更新 1 根据 Stefano M 的建议,我做了一份 F2PY 性能报告。
/-----------------------\
< F2PY performance report >
\-----------------------/
Overall time spent in ...
(a) wrapped (Fortran/C) functions : 105 msec
(b) f2py interface, 1 calls : 2925 msec
(c) call-back (Python) functions : 0 msec
(d) f2py call-back interface, 0 calls : 0 msec
(e) wrapped (Fortran/C) functions (acctual) : 105 msec
可以看出几乎所有时间都在界面中使用!下面是如何调用函数:
import numpy as np
import time
import fortranrun
A = np.random.rand(100,100,100,100)
start = time.time()
B = fortranrun.f2py_run(A)
print(time.time()-start, B, 'f2py')