Дошли руки до Cythona, спасибо самоизоляции. Проблема прозаична — как ускориться на python с минимальными потерями в синтаксисе. Один из подходов — использование Сython (смесь С и python).
Не давала покоя публикация с громким названием. Но из содержания публикации мало что можно вынести, так как формулы и результирующая таблица неверны. Попробуем дополнить картину, начатую авторами поста и расставим точки над и.
*Тесты проводились на odroid xu4, ubuntu mate, python 2.7.17.
Cython ставится просто (pip install cython).
Будем мучить все те же числа Фибоначчи. Создадим файлы для тестов прироста производительности. Для языка python (test.py):
def test(n):
a, b = 0.0, 1.0
for i in range(n):
a, b = a + b, a
print (a)
Для языка cython(test2.pyx):
def test2(int n):
cdef int i
cdef double a=0.0, b=1.0
for i in range(n):
a, b = a + b, a
print (a)
Файл cython требует предварительной сборки. Для него создадим setup.py c содержимым:
from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize('test2.pyx'))
И соберем:
python setup.py build_ext --inplace
Теперь возьмем файл из упомянутого поста с тестами и немного его поправим, добавив возможность вводить собственное число на старте (tests.py):
import test
import test2
import time
number = input('enter number: ')
start = time.time()
test.test(number)
end = time.time()
py_time = end - start
print("Python time = {}".format(py_time))
start = time.time()
test2.test(number)
end = time.time()
cy_time = end - start
print("Cython time = {}".format(cy_time))
print("Speedup = {}".format(py_time / cy_time))
Посмотрим, что получилось:
python tests.py
Результаты:
Для python 2.7:
enter number: 10
Python time = 1.62124633789e-05
Cython time = 4.05311584473e-06
Speedup = 4.0
enter number: 100
Python time = 3.40938568115e-05
Cython time = 5.00679016113e-06
Speedup = 6.80952380952
enter number: 1000
Python time = 0.000224113464355
Cython time = 1.19209289551e-05
Speedup = 18.8
enter number: 100000
Python time = 0.0200171470642
Cython time = 0.000855922698975
Speedup = 23.3866295265
Для python 3:
enter number: 10
Python time = 7.653236389160156e-05
Cython time = 2.8133392333984375e-05
Speedup = 2.7203389830508473
enter number: 100
Python time = 8.678436279296875e-05
Cython time = 3.170967102050781e-05
Speedup = 2.736842105263158
enter number: 1000
Python time = 0.00031876564025878906
Cython time = 4.673004150390625e-05
Speedup = 6.821428571428571
enter number: 100000
Python time = 0.01643967628479004
Cython time = 0.0004260540008544922
Speedup = 38.5858981533296
*модуль test2.pyx «пересобирался» командой:
python3 setup.py build_ext --inplace
**устанавливался cython:
pip3 install cython
Можно обойтись без сборки test2.pyx с использованием setup.py, для этого необходимо просто в файл tests.py добавить строки:
import pyximport
pyximport.install()
Теперь test2.pyx будет собираться на лету при каждом запуске tests.py, а файлов в папке будет меньше.
Как завести cython на windows.
Несмотря на то, что cython допускает сборку файлов как под python3, так и python2, получить готовый рецепт под python3 не удалось.
С python3 cтандартная команда сборки не работает:
python setup.py build_ext --inplace
Также не дает результата сборка с использованием стороннего компилятора mingw:
python setup.py build_ext -i --compiler=mingw32
*Не работают также варианты:
python setup.py build_ext -i --compiler=mingw32 -DMS_WIN64
и
python setup.py build_ext -i --compiler=msvc
Поэтому рабочий вариант, который позволит поработать в windows c cython, использует python2.7 и компилятор mingw.
Процедура следующая.
1.Устанавливаем сам cython:
pip install cython
2.Устанавливаем компилятор mingw:
mingw
3.После установки компилятора и добавления его в PATH windows файл формата .pyx можно собирать командой:
python setup.py build_ext -i --compiler=mingw32
iroln
Опять hello world с бессмысленными "бенчмарками". Ради приличия взяли бы хоть раз какую-то реальную bottleneck проблему сложнее Фибоначчи и переложили бы её на Cython. Сразу бы прочувствовали на реальном опыте "минимальные потери в синтаксисе" и прочие грабли.
Если это совет так делать всегда, то это вредный совет. Такое годится только для экспериментального кода на вашей машине. Это не для применения в распространяемом коде библиотек и приложений. Вы же не хотите заставить ваших пользователей иметь C-компилятор и настроенное окружение для сборки Cython-кода для запуска вашего проекта? Cython-изация должна выполняться только на этапе сборки пакета (Cython должен быть только в setup_requires). Если вы распространяете бинарные wheel-ы своего пакета с pyx-файлами, то там должны лежать уже собранные модули расширения и никаких исходников в pyx и никакой зависимости от Cython в runtime.
zoldaten Автор
Спасибо за коммент.
Для реальной задачи необходим рабочий «hello world». Как показывает практика, некоторые даже до этого не доживают.
Про ремарку об упрощении без setup.py — все верно. Для продакшн это не годится, но упрощает локальную разработку.