Одно простое, но существенное ускорение - вывести умножение на А за пределы вашей суммы. Вы можете просто умножить B с ним, как только вернете его:
for i in range(len(relative)):
#work out the dipole field and add it to the estimate so far
B += (3*dot(mom_i[i],r_unit[i])*r_unit[i] - mom_i[i]) / sqrt(dot(relative[i],relative[i]))**3
return A*B
Это дало примерно 8% ускорение при использовании 20 000 случайных диполей.
Помимо этого простого ускорения, я бы порекомендовал использовать Cython (который обычно рекомендуется вместо Pyrex) или Weave от Scipy. Взгляните на Performance Python для некоторых примеров и сравнений различных способов ускорения Numpy / Scipy.
Если вы хотите попробовать эту параллель, я бы порекомендовал взглянуть на Параллельное программирование Сципи , чтобы начать.
Приятно видеть другого физика на SO. Здесь их не очень много.
Edit:
Я решил принять это как вызов для развития некоторых навыков Cython и получил улучшение примерно в 10 раз по сравнению с оптимизированной для Psyco версией. Дайте мне знать, если вы хотите увидеть мой код.
Edit2:
Ладно, вернулся и обнаружил, что тормозит в моей версии Cython. Теперь ускорение превышает 100 раз. Если вы хотите или нуждаетесь в другом коэффициенте в 2 раза превышающем ускоренную версию Ray Numpy, дайте мне знать, и я опубликую свой код.
Исходный код Cython:
Вот код Cython, который я набрал:
import numpy as np
cimport numpy as np
cimport cython
cdef extern from "math.h":
double sqrt(double theta)
ctypedef np.float64_t dtype_t
@cython.boundscheck(False)
@cython.wraparound(False)
def calculate_dipole_cython(np.ndarray[dtype_t,ndim=2,mode="c"] mu,
np.ndarray[dtype_t,ndim=2,mode="c"] r_i,
np.ndarray[dtype_t,ndim=2,mode="c"] mom_i):
cdef Py_ssize_t i
cdef np.ndarray[dtype_t,ndim=1,mode="c"] tmp = np.empty(3,np.float64)
cdef np.ndarray[dtype_t,ndim=1,mode="c"] relative = np.empty(3,np.float64)
cdef double A = 1e-7
cdef double C, D, F
cdef np.ndarray[dtype_t,ndim=1,mode="c"] B = np.zeros(3,np.float64)
for i in xrange(r_i.shape[0]):
relative[0] = mu[0,0] - r_i[i,0]
relative[1] = mu[0,1] - r_i[i,1]
relative[2] = mu[0,2] - r_i[i,2]
C = relative[0]*relative[0] + relative[1]*relative[1] + relative[2]*relative[2]
C = 1.0/sqrt(C)
D = C**3
tmp[0] = relative[0]*C
F = mom_i[i,0]*tmp[0]
tmp[1] = relative[1]*C
F += mom_i[i,1]*tmp[1]
tmp[2] = relative[2]*C
F += mom_i[i,2]*tmp[2]
F *= 3
B[0] += (F*tmp[0] - mom_i[i,0])*D
B[1] += (F*tmp[1] - mom_i[i,1])*D
B[2] += (F*tmp[2] - mom_i[i,2])*D
return A*B
Я немного оптимизировал это, я думаю, но может быть немного больше, что вы можете извлечь из этого. Вы все еще можете заменить np.zeros и np.empty прямыми вызовами из Numpy C API, но это не должно иметь большого значения. В нынешнем виде этот код дает в 2-3 раза больше, чем у оптимизированного кода Numpy. Тем не менее, вам нужно правильно ввести числа. Массивы должны быть в формате C (который используется по умолчанию для массивов Numpy, но в Numpy транспонирование массива в формате C представляет собой массив в формате Fortran).
Например, чтобы запустить код из вашего другого вопроса , вам нужно заменить np.random.random((3,N))
s на np.random.random((N,3))
. Кроме того, `
r_test_fast = reshape_vector(r_test)
необходимо изменить на
r_test_fast = np.array(np.matrix(r_test))
Эта последняя строка может быть сделана проще / быстрее, но, по моему мнению, это было бы преждевременной оптимизацией.
Если вы раньше не использовали Cython и не знаете, как его скомпилировать, сообщите мне, и я буду рад помочь.
Наконец, я бы порекомендовал посмотреть эту статью . Я использовал это в качестве руководства для моей оптимизации. Следующим шагом будет попытка использовать функции BLAS, которые используют набор инструкций SSE2, попытка использования API SSE или попытка использовать больше API Numpy C, который взаимодействует с SSE2. Также вы можете посмотреть на распараллеливание.