为什么我的 f2py 程序比 python 程序慢
Why is my f2py programs slower than python programs
最近用python写了一个耗时的程序,决定用fortran重写最耗时的部分
但是,用 f2py 包装的 fortran 代码比 python 代码慢,谁能告诉我如何找到这里发生的事情?
作为参考,这里是 python 函数:
def iterative_method(alpha0, beta0, epsilon0, epsilons0, omega, smearing=0.01, precision=0.01, max_step=20, flag=0):
# alpha0, beta0, epsilon0, epsilons0 are numpy arrays
m, n = np.shape(epsilon0)
Omega = np.eye(m, dtype=np.complex) * (omega + smearing * 1j)
green = LA.inv(Omega - epsilon0) # LA is numpy.linalg
alpha = np.dot(alpha0, np.dot(green, alpha0))
beta = np.dot(beta0, np.dot(green, beta0))
epsilon = epsilon0 + np.dot(alpha0, np.dot(green, beta0)) + np.dot(beta0, np.dot(green, alpha0))
epsilons = epsilons0 + np.dot(alpha0, np.dot(green, beta0))
while np.max(np.abs(alpha0)) > precision and np.max(np.abs(beta0)) > precision and flag < max_step:
flag += 1
return iterative_method(alpha, beta, epsilon, epsilons, omega, smearing, precision, min_step, max_step, flag)
return epsilon, epsilons, flag
对应的fortran代码为
SUBROUTINE iterate(eout, esout, alpha, beta, e, es, omega, smearing, prec, max_step, rank)
INTEGER, PARAMETER :: dp = kind(1.0d0)
REAL(kind=dp) :: omega, smearing, prec
INTEGER :: max_step, step, rank, cnt
COMPLEX(kind=dp) :: alpha(rank,rank), beta(rank,rank), omega_mat(rank, rank),&
green(rank, rank), e(rank,rank), es(rank,rank)
COMPLEX(kind=dp), INTENT(out) :: eout(rank, rank), esout(rank, rank)
step = 0
omega_mat = 0
DO cnt=1, rank
omega_mat(cnt, cnt) = 1.0_dp
ENDDO
omega_mat = omega_mat * (omega + (0.0_dp, 1.0_dp) * smearing)
DO WHILE (maxval(abs(alpha)) .gt. prec .or. maxval(abs(beta)) .gt. prec .and. step .lt. max_step)
green = zInverse(rank, omega_mat - e) ! zInverse is calling lapack to compute inverse of the matrix
e = e + matmul(alpha, matmul(green, beta)) + matmul(beta, matmul(green, alpha))
es = es + matmul(alpha, matmul(green, beta))
alpha = matmul(alpha, matmul(green, alpha))
beta = matmul(beta, matmul(green, beta))
step = step + 1
ENDDO
print *, step
eout = e
esout = es
END SUBROUTINE iterate
在一次测试中,python代码用了大约5秒,而fortran代码用了大约7秒,这很难接受。此外,我几乎看不到 Fortran 代码有任何开销。包装器应该受到指责吗?
编辑: 我没有将 BlAS
用于 matmul
。使用BLAS
后,fortran和python的性能都在5秒左右。
首先,对 python 代码执行 this,这样您就可以确切地知道它是如何花费时间的。
然后,如果愿意,您可以使用调试器对 Fortran 代码执行类似的操作。
我怀疑基本上 所有 的时间都用于矩阵运算,因此任何速度差异都是由于数学库造成的,而不是调用它的语言造成的。
This post 传达了我这样做的一些经验。
通常,执行诸如矩阵乘法、逆向或 Cholesky 变换之类的例程旨在对大矩阵有效,但对小矩阵无效。
例如,LAPACK 矩阵乘法例程 DGEMM 有两个字符参数,TRANSA 和 TRANSB,可以是大写或小写,指定每个输入矩阵是否转置。
为了检查这些参数的值,它调用了一个函数 LSAME。
我发现,如果我花大部分时间乘以小矩阵,如 4x4,程序实际上几乎所有时间都花在调用 LSAME 上,而真正乘以矩阵的时间很少。
您可以看到如何轻松解决这个问题。
最近用python写了一个耗时的程序,决定用fortran重写最耗时的部分
但是,用 f2py 包装的 fortran 代码比 python 代码慢,谁能告诉我如何找到这里发生的事情?
作为参考,这里是 python 函数:
def iterative_method(alpha0, beta0, epsilon0, epsilons0, omega, smearing=0.01, precision=0.01, max_step=20, flag=0):
# alpha0, beta0, epsilon0, epsilons0 are numpy arrays
m, n = np.shape(epsilon0)
Omega = np.eye(m, dtype=np.complex) * (omega + smearing * 1j)
green = LA.inv(Omega - epsilon0) # LA is numpy.linalg
alpha = np.dot(alpha0, np.dot(green, alpha0))
beta = np.dot(beta0, np.dot(green, beta0))
epsilon = epsilon0 + np.dot(alpha0, np.dot(green, beta0)) + np.dot(beta0, np.dot(green, alpha0))
epsilons = epsilons0 + np.dot(alpha0, np.dot(green, beta0))
while np.max(np.abs(alpha0)) > precision and np.max(np.abs(beta0)) > precision and flag < max_step:
flag += 1
return iterative_method(alpha, beta, epsilon, epsilons, omega, smearing, precision, min_step, max_step, flag)
return epsilon, epsilons, flag
对应的fortran代码为
SUBROUTINE iterate(eout, esout, alpha, beta, e, es, omega, smearing, prec, max_step, rank)
INTEGER, PARAMETER :: dp = kind(1.0d0)
REAL(kind=dp) :: omega, smearing, prec
INTEGER :: max_step, step, rank, cnt
COMPLEX(kind=dp) :: alpha(rank,rank), beta(rank,rank), omega_mat(rank, rank),&
green(rank, rank), e(rank,rank), es(rank,rank)
COMPLEX(kind=dp), INTENT(out) :: eout(rank, rank), esout(rank, rank)
step = 0
omega_mat = 0
DO cnt=1, rank
omega_mat(cnt, cnt) = 1.0_dp
ENDDO
omega_mat = omega_mat * (omega + (0.0_dp, 1.0_dp) * smearing)
DO WHILE (maxval(abs(alpha)) .gt. prec .or. maxval(abs(beta)) .gt. prec .and. step .lt. max_step)
green = zInverse(rank, omega_mat - e) ! zInverse is calling lapack to compute inverse of the matrix
e = e + matmul(alpha, matmul(green, beta)) + matmul(beta, matmul(green, alpha))
es = es + matmul(alpha, matmul(green, beta))
alpha = matmul(alpha, matmul(green, alpha))
beta = matmul(beta, matmul(green, beta))
step = step + 1
ENDDO
print *, step
eout = e
esout = es
END SUBROUTINE iterate
在一次测试中,python代码用了大约5秒,而fortran代码用了大约7秒,这很难接受。此外,我几乎看不到 Fortran 代码有任何开销。包装器应该受到指责吗?
编辑: 我没有将 BlAS
用于 matmul
。使用BLAS
后,fortran和python的性能都在5秒左右。
首先,对 python 代码执行 this,这样您就可以确切地知道它是如何花费时间的。 然后,如果愿意,您可以使用调试器对 Fortran 代码执行类似的操作。
我怀疑基本上 所有 的时间都用于矩阵运算,因此任何速度差异都是由于数学库造成的,而不是调用它的语言造成的。 This post 传达了我这样做的一些经验。 通常,执行诸如矩阵乘法、逆向或 Cholesky 变换之类的例程旨在对大矩阵有效,但对小矩阵无效。
例如,LAPACK 矩阵乘法例程 DGEMM 有两个字符参数,TRANSA 和 TRANSB,可以是大写或小写,指定每个输入矩阵是否转置。 为了检查这些参数的值,它调用了一个函数 LSAME。 我发现,如果我花大部分时间乘以小矩阵,如 4x4,程序实际上几乎所有时间都花在调用 LSAME 上,而真正乘以矩阵的时间很少。 您可以看到如何轻松解决这个问题。