Pull to refresh

Comments 31

Cython это правильный олдскульный вариант для ускорения Python кода. Но я давно всем рекомендую Numba.

Одним декоратором можно добиться такой же скорости, как и у Cython без всяких .pyx модулей и С-подобного языка. Вот рабочий код для факториала:

from numba import jit

@jit
def test(x):
    y = 1
    for i in range(1, x+1):
        y *= i
    return y
UFO just landed and posted this here
Бешено плюсую Numba для численных вычислений в цикле и особенно в цикле в цикле.
Она даже больше чем Cython ускоряет в каких-то случаях, потому что может параллельно выполнять код и другие трюки использовать.
А если одно ядро?
Я имею ввиду веб-сервер где количество ядер ограничено
Веб-сервер надо вообще другими способами ускорять. Nginx там всякий и т.п. )) Шутка. Надо смотреть что за код, какие задачи. Может там не Python надо, а Go, например. Кто его знает.
Склоняюсь к написанию внешней функции на C, и так NGINX UNIT трудится, правда на 512 мегабайтах (
Если надо экономить и время и память и в питоне нет готовых библиотек, оптимизированных под ваши нужды, то писать на С — самое то. У питона все оптимизированные модули либо на С либо на современном фортране написаны.
Для простых вычислительных функций numba идеальный вариант. Трудности возникают при использовании разнородных и сложноструктурированных даннх к которым numba плохо приспособлен. Да, там есть jitclass но он оставляет желать лучшего. Если взаимодействующих объектов много то со временем становится сложно понять какой код генерит numba и откуда валятся ошибки. В этот момент задумываешься о преимуществах Cython где все декларируется явным образом. Если хорошо знаком с C++ то он может быть предпочтительным вариантом несмотря на бОльший входной порог.
UFO just landed and posted this here
Для факториала таки лучше выше упомянутая Numba )
Небольшое уточнение: для факториала аргумента, не большего 25.
Единственное изменение Python-кода при этом заключается в добавлении к каждой переменной информации об её типе.

Если требуется пройти по всему коду, то может лучше тогда заодно на go переписать?

Боже, что вы намерили? Результат функции test(100000) — это число с 456574 знаками. Не число 456574, а число с 456574 знаками.


$ ipython
Python 3.7.3 (default, Apr  3 2019, 19:16:38) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: def test(x): 
   ...:     y = 1 
   ...:     for i in range(1, x+1): 
   ...:         y *= i 
   ...:     return y 
   ...:                                                                                                                         

In [2]: %time a = test(100000)                                                                                                  
CPU times: user 1.76 s, sys: 3.57 ms, total: 1.76 s
Wall time: 1.76 s

In [3]: len(str(a))                                                                                                             
Out[3]: 456574

Время работы от увеличения количество итераций должно расти явно не линейно, так как длина арифметики тоже увеличивается. Каким образом у вас это заняло 2 миллисекунды?


А в Cython тип int — это 32-bit signed integer: https://nyu-cds.github.io/python-cython/01-syntax/
Соответственно результат работы функции на Cython просто неверный.

Если вы имели в виду, что результат должен быть типа double (то есть y = 1.0), то результат будет бесконечность уже для test(171).

Коммент об этом есть к оригиналу сего шедевра.

Разгадка, если кому интересно.


В оригинальном посте на Медиуме была ошибка, из-за которой результат функции всегда был ноль!


def test(x):
    y = 1
    for i in range(x):
        y *= i
    return y

Замеры были сделаны именно для этой версии, и тут нет длинной арифметики.


Автор оригинального поста решил не париться и убрал неверный код и результаты, оставив голословные утверждения о 36-кратном приросте производительности непонятно чего и упоминание некой таблицы, которой в посте нет:


Check out the table below which shows how much speed Cython gave us for different factorial values. We got over 36X speedup with Cython!

Авторы же перевода видимо захотели исправить код, получили работающую в тысячи раз медленнее версию на питоне и неверно работающую версию на Cython.

Никогда не писал на питоне и не знаком с языком вообще, но ситуацию, когда трансляция кода в С будет работать не так, как хотелось и как работал бы оригинал, предвидел сразу. С таким же успехом можно написать свой «компилятор» для любого языка, наделав и спрятав кучу ошибок.
Наверно лучше сделать C dll и вынести тяжелый цикл в нее. Я так у себе сделал когда надо было бегать по двойному циклу, и python задумывался. Конечно время зависело от железа. На мощных ПК может и не особо, а вот на слабых машинах очень и очень долго. Вынеся его в dll отработка цикла происходит очень быстро и главное даже на слабых ПК работает так же быстро.
Не стоит думать о Cython как о «быстром Python». Это скорее C похожим на Python синтаксисом.
Всегда когда я пробывал использовать CPython он или не работал, или не давал разницы больше 5%. Может в какихто тяжелых вычислениях он и показывает это преимущество, но мне их тогда проще написать на C и включить модулем.
С puppy та же петрушка, но при использовании falcon pypy таки дает гдето 10-20%(из обещаных 200+)
Всегда при упоминании Cython задавал себе вопрос, а нельзя разве написать такой инструмент, чтобы автоматически преобразовывать питоновский код в ситоновский? Ведь это должно быть просто, если на самом деле разница только в объявлении типов.
Там такая разница, что от Питона остается только хвостик )
Вполне может быть. Но в статье почему то говорится, что
Единственное изменение Python-кода при этом заключается в добавлении к каждой переменной информации об её типе.

То есть, вроде как всего ничего.
Так pypy же в теории пытаются это сделать. Но чегото не особо получается.
В питоне типы не известны статически, поэтому «разница только в объявлении типов» — это не только не просто, но и нерешаемо в общем случае.
UFO just landed and posted this here
Если по уму такой транслятор делать, то он тог бы такие переменные отслеживать и создавать для каждого типа новые. Ну или хотя бы выдавать ошибку в месте, где тип меняется. Так как это, всё-таки, не очень хорошо, менять тип переменной по ходу выполнения.

Cython и пытается это делать, можно python код без изменений оставить, но будет работать медленнее:
http://docs.cython.org/en/latest/src/tutorial/pure.html


In some cases, it’s desirable to speed up Python code without losing the ability to run it with the Python interpreter. While pure Python scripts can be compiled with Cython, it usually results only in a speed gain of about 20%-50%.
    cdef int i
    for i in range(1, x+1):
        y *= i
А после такого объявления переменная будет видна после цикла или нет?
Будет видна, конечно. А к чему ваш вопрос, интересно?
К тому, что я тупой. Просто почему-то подумал, что в питончике переменная цикла видна только внутри цикла. Можете не обращать внимания.
Sign up to leave a comment.