Ускорить 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))
Другие вопросы по тегам