I tried to used f2py, Cython and Numba to make a simple loop structure be faster in python. Python implementation:
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 implementation:
@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 implementation:
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 implementation:
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 compilation:
setup(ext_modules=cythonize(Extension('runtest',['runtest.pyx'])), include_dirs=[numpy.get_include()])
f2py compilation:
f2py -c runFortran.F90 --f90flags=-O3 -m fortranrun
The functions was run by passing a np.random.rnd(100,100,100,100) into the functions. The timings were as follows:
22.998 sec; Python
0.114 sec; Cython
2.678 sec; f2py
0.106 sec; numba
I know that for this specific problem I have set up, I can use np.einsum(). np.einsum() had a timing of 0.061 sec. But for my "real" code the problem cannot be solved with einsum. It can be seen that Cython and Numba executes at about the same speed, whereas f2py is much slower. The f2py function is written in Fortran90, so I would have thought it would have been atleast as fast. My question is thus, am I using f2py wrongly? Or does f2py have speed problems?
UPDATE 1 By suggestion from Stefano M, I made a F2PY perfermance report.
/-----------------------\
< 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
As it can be seen almost all the time is used in the interface! Here is how to function is called:
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')
l, k, j, i
was faster. Might it be related to how the array is stored in numpy? docs.scipy.org/doc/numpy/f2py/python-usage.html#array-arguments $\endgroup$