Я написал функцию Python, которая вычисляет попарно электромагнитные взаимодействия между большим числом (N ~ 10^3) частиц и сохраняет результаты в комплексе NxN128 ndarray. Он работает, но это самая медленная часть более крупной программы, занимающая около 40 секунд, когда N = 900 [исправлено]. Исходный код выглядит следующим образом:Ускорение Cython не так велико, как ожидалось
import numpy as np
def interaction(s,alpha,kprop): # s is an Nx3 real array
# alpha is complex
# kprop is float
ndipoles = s.shape[0]
Amat = np.zeros((ndipoles,3, ndipoles, 3), dtype=np.complex128)
I = np.array([[1,0,0],[0,1,0],[0,0,1]])
im = complex(0,1)
k2 = kprop*kprop
for i in range(ndipoles):
xi = s[i,:]
for j in range(ndipoles):
if i != j:
xj = s[j,:]
dx = xi-xj
R = np.sqrt(dx.dot(dx))
n = dx/R
kR = kprop*R
kR2 = kR*kR
A = ((1./kR2) - im/kR)
nxn = np.outer(n, n)
nxn = (3*A-1)*nxn + (1-A)*I
nxn *= -alpha*(k2*np.exp(im*kR))/R
else:
nxn = I
Amat[i,:,j,:] = nxn
return(Amat.reshape((3*ndipoles,3*ndipoles)))
Я никогда раньше не использовал Cython, но это казалось как хорошее место, чтобы начать в моих усилиях, чтобы ускорить вещи, так что я в значительной степени вслепую адаптировал методы, которые я нашел в Интернете учебные пособия. Я получил ускорение (30 секунд против 40 секунд), но не так драматично, как я ожидал, поэтому мне интересно, что я делаю что-то неправильно или не вижу критического шага. Ниже моя лучшая попытка cythonizing вышеуказанную процедуру:
import numpy as np
cimport numpy as np
DTYPE = np.complex128
ctypedef np.complex128_t DTYPE_t
def interaction(np.ndarray s, DTYPE_t alpha, float kprop):
cdef float k2 = kprop*kprop
cdef int i,j
cdef np.ndarray xi, xj, dx, n, nxn
cdef float R, kR, kR2
cdef DTYPE_t A
cdef int ndipoles = s.shape[0]
cdef np.ndarray Amat = np.zeros((ndipoles,3, ndipoles, 3), dtype=DTYPE)
cdef np.ndarray I = np.array([[1,0,0],[0,1,0],[0,0,1]])
cdef DTYPE_t im = complex(0,1)
for i in range(ndipoles):
xi = s[i,:]
for j in range(ndipoles):
if i != j:
xj = s[j,:]
dx = xi-xj
R = np.sqrt(dx.dot(dx))
n = dx/R
kR = kprop*R
kR2 = kR*kR
A = ((1./kR2) - im/kR)
nxn = np.outer(n, n)
nxn = (3*A-1)*nxn + (1-A)*I
nxn *= -alpha*(k2*np.exp(im*kR))/R
else:
nxn = I
Amat[i,:,j,:] = nxn
return(Amat.reshape((3*ndipoles,3*ndipoles)))
Numpy - это библиотека C. И использует BLAS для выполнения алгебры, так что это довольно быстро. Я действительно не понимаю, как работает внутренняя среда cython, но, будучи уже запутанным C-кодом, выигрыш в скорости ничем «не numpy». –
Я предположил, что достаточное количество операций по строке внутри вложенного цикла требует прямого вызова интерпретатора Python и поэтому эти строки, вероятно, являются доминирующей стоимостью относительно Numpy, но, возможно, нет? –
Вы можете попробовать ввести массивы numpy, чтобы компилятор знал типы внутри массивов. Не уверен, насколько велика разница. Возможно, вы захотите запустить профилировщик кода python, чтобы увидеть, где вы фактически теряете скорость. Если большую часть времени тратится в numpy-процедурах, вы не получите многого, используя cython. – cel