Python в настоящее время стал довольно популярным и универсальным языком. Во много благодаря своему удобному синтаксису, огромному количеству библиотек и простой расширяемости сишными модулями. Конечно когда говорят о скорости, все дружно морщат нос. Однако, в приложениях из реальной жизни, при привальном подходе, вы скорее упретесь в базу или сеть, чем в Python. Конечно есть всякие интересные вычисления, где интерпретатор дает большие накладные расходы и Gil не сильно радует, нет типов для возможных оптимизаций и упрощенного отлова ошибок. Все так, но не совсем.
В python есть такая вещь как
Type Hints. Мы просто можем указать типы данных для передаваемых значений в функции, а также типы возвращаемых значений.
def greeting(name: str) -> str:
return 'Hello ' + name
Конечно, для интерпретатора, это мало, что значит. Основная идея была в улучшении подсказок в средах разработки, упрощенного анализа кода и поиска багов, может помочь вам писать код строже (потому что вы начнете соблюдать типы и думать о структуре кода, а не пихать все в одну переменную, если вы конечно так делали), так же это может помочь статическим анализаторам кода в нахождении проблем. Штука интересная, но не сильно помогающая в продакшене. Но на самом деле, теперь мы можем сделать очень интересные вещи для ускорения кода. И нам поможет Cython. В детали не будем залазить, возьмем простой пример показывающий общую суть.
def fun(x: float) -> float:
return x*x-x
def integrate_f(a: float, b: float, N: int):
s : float = 0
dx : float = (b - a) / N
for i in range(N):
s += fun(a + i * dx)
return s * dx
Вот пример нашего кода на питоне (прямо из документации). Проверяем:
begin = time.time()
print(tpy.integrate_f(1006, 2610, 100000000))
print(time.time() - begin)
Итог:
5584257516.163277
25.64701795578003
25 секунд, не очень радостное время. Однако, если мы переименуем файл в pyx и дополним тремя декораторами:
import cython
@cython.cfunc # cdef functions are faster but not callable from python
def fun(x: float) -> float:
return x*x-x
@cython.locals(i=cython.int, N=cython.int) # better integration soon
@cython.cdivision(True) # remove divide by zero protection
def integrate_f(a: float, b: float, N: int):
s : float = 0
dx : float = (b - a) / N
for i in range(N):
s += fun(a + i * dx)
return s * dx
Как это добро компилировать смотрим в
документации. Проверяем:
import t (наш cython модуль)
import time
if __name__ == '__main__':
begin = time.time()
print(t.integrate_f(1006, 2610, 100000000))
print(time.time() - begin)
Итог:
5584257516.166599
0.15540504455566406
Результат более, чем впечатляет. Затраченных усилий минимум и увеличение в поддержке кода не значительное. Конечно чем сложнее куски, тем сложнее будет соблюдать типизацию, некоторые вещи могут вообще не дать видимого прироста. Однако, если вы уперлись в Python, то можно попробовать использовать Cython для получения профита. В каждой конкретной задаче нужно смотреть оправданность усилий и полученного результата, но в целом профит может быть велик.
Для сравнения делаем в лоб одинаковое решение на go:
package main
import (
"log"
"time"
)
func fun(x float32) float32 {
return x*x - x
}
func integrate_f(a float32, b float32, N int) float32 {
s := float32(0.0)
dx := (b - a) / float32(N)
for i := 0; i < N; i++ {
s += fun(a + float32(i)*dx)
}
return s * dx
}
func main() {
start := time.Now()
integrate_f(1006, 2610, 100000000)
elapsed := time.Since(start)
log.Printf("time %s", elapsed)
}
Получим 0,105165. В данном случаи выгрыш от cython не значителен. Я это к тому, что в каждой задаче хорошо свое решение и пытаться переписать все на go не решение всех проблем.