Ускорить Python-код (несколько интегральных расчетов)
Есть ли способ ускорить этот код:
import mpmath as mp
import numpy as np
from time import time as epochTime
def func(E):
f = lambda theta: mp.sin(theta) * mp.exp(E * (mp.cos(theta**2) + \
mp.cos(theta)**2))
return f
start = epochTime()
mp.mp.dps = 15
mp.mp.pretty = True
E = np.linspace(0, 10, 200)
ints = [mp.quadgl(func(e), [0, mp.pi]) for e in E] # Main Job
print ('Took:{:.3}s'.format(epochTime() - start))
2 ответа
Запустив ваш код, я рассчитал его до 5,84 с
с помощью Memoize
и упрощающие выражения:
cos = Memoize(mp.cos)
sin = Memoize(mp.sin)
def func(E):
def f(t):
cost = cos(t)
return sin(t) * mp.exp(E * (cos(t*t) + cost*cost))
return f
Я снизил его до 3,25 с в первый раз и до 2,8 с в следующих итерациях.
(Еще лучший подход мог бы использовать lru_cache
из стандартной библиотеки, но я не пытался рассчитать это).
Если вы выполняете подобный код много раз, это может быть целесообразно для Memoize()
и то и другое func
а также f
, поэтому вычисления становятся тривиальными ( ~0.364 с).
Замена mp
с math
для cos/sin/exp я опустился до ~1,3 с, и теперь запоминание по какой-то причине ухудшает производительность (~1,5 с, я думаю, время поиска стало доминирующим).
В общем, вы хотите как можно больше избегать вызовов трансцендентных функций, таких как sin, cos, exp, ln, особенно в "горячей" функции, такой как интеграл.
- Замените x**2 на x*x (часто x**2 вызывает обобщенную = медленную функцию возведения в степень)
- используйте переменные для "дорогих" промежуточных терминов, которые используются более одного раза
- преобразовать ваше уравнение, чтобы уменьшить или устранить трансцендентные функции
- особый случай для типичных значений параметров. Целые показатели - частый кандидат.
- предварительно вычислить все, что является постоянным, особенно в параметризованных функциях
Для конкретного примера вы можете заменить z=cos(theta). Это дз = -син (тета) дхета. Ваша интеграция становится
-exp(E*(z^2 + cos(arccos(z)^2))
спасая вас от некоторых трансцендентных вызовов функций. Границы [0, pi] становятся [1, -1]. Также избегайте х **2, лучше используйте х * х.
Полный код:
import mpmath as mp
import numpy as np
from time import time as epochTime
def func(E):
def f(z):
acz = mp.acos(z)
return -mp.exp(E * (mp.cos(acz*acz) + z*z))
return f
start = epochTime()
mp.mp.dps = 15
mp.mp.pretty = True
E = np.linspace(0, 10, 200)
ints = [mp.quadgl(func(e), [1.0, -1.0]) for e in E] # Main Job
print ('Took:{:.3}s'.format(epochTime() - start))