Хабрахабр

[Перевод] Cython: более чем 30-кратное ускорение Python-кода

Python — это язык, который любят многие программисты. Этим языком невероятно легко пользоваться. Всё дело в том, что код, написанный на Python, отличается интуитивной понятностью и хорошей читабельностью. Однако в разговорах о Python часто можно слышать одну и ту же жалобу на этот язык. Особенно тогда, когда о Python говорят знатоки языка C. Вот как она звучит: «Python — это медленно». И те, кто так говорят, не грешат против истины.

Вот результаты испытаний, в ходе которых сопоставляется производительность разных языков программирования при решении различных задач. В сравнении со многими другими языками программирования Python — это, и правда, медленно.

Например, можно применять библиотеки, рассчитанные на использование нескольких ядер процессора. Есть несколько способов ускорения Python-программ. Например — это задачи по предварительной обработке данных или операции с матрицами. Тем, кто работает с Numpy, Pandas или Scikit-Learn, можно посоветовать взглянуть на программный комплекс Rapids, позволяющий задействовать GPU при проведении научных расчётов.
Все эти методики ускорения работы хороши в тех случаях, когда решаемые с помощью Python задачи могут быть распараллелены.

Что если у вас есть большой цикл for, который вам совершенно необходимо использовать, и выполнение которого просто нельзя распараллелить из-за того, что обрабатываемые в нём данные должны обрабатываться последовательно? Но как быть в том случае, если ваш код — это чистый Python? Можно ли как-то ускорить сам Python?

Ответ на этот вопрос даёт Cython — проект, используя который можно значительно ускорить код, написанный на Python.

Что такое Cython?

Cython, по своей сути, это промежуточный слой между Python и C/C++. Cython позволяет писать обычный Python-код с некоторыми незначительными модификациями, который затем напрямую транслируется в C-код.

При написании обычного кода на Python переменную можно объявить так: Единственное изменение Python-кода при этом заключается в добавлении к каждой переменной информации об её типе.

x = 0.5

При использовании Cython при объявлении переменной нужно указать её тип:

cdef float x = 0.5

Эта конструкция сообщает Cython о том, что переменная представляет собой число с плавающей точкой. По такому же принципу объявляют переменные и в C. При использовании обычного Python типы переменных определяются динамически. Явное объявление типов, применяемое в Cython — это то, что делает возможным преобразование Python-кода в C-код. Дело в том, что в C необходимо явное объявление типов переменных.

Установка Cython предельно проста:

pip install cython

Типы в Cython

При использовании Cython можно выделить два набора типов. Один — для переменных, второй — для функций.

Если речь идёт о переменных, то тут нам доступны следующие типы:

  • cdef int a, b, c
  • cdef char *s
  • cdef float x = 0.5 (число одинарной точности)
  • cdef double x = 63.4 (число двойной точности)
  • cdef list names
  • cdef dict goals_for_each_play
  • cdef object card_deck

Обратите внимание на то, что тут, фактически, показаны типы C/C++!

При работе с функциями нам доступны следующие типы:

  • def — обычная Python-функция, вызывается только из Python.
  • cdef — Cython-функция, которую нельзя вызвать из обычного Python-кода. Такие функции можно вызывать только в пределах Cython-кода.
  • cpdef — Функция, доступ к которой можно получить и из C, и из Python.

Теперь, когда мы разобрались с типами Python, можно заняться ускорением Python-кода.

Ускорение кода с использованием Cython

Начнём с создания Python-бенчмарка. Это будет цикл for, в котором выполняется вычисление факториала числа. Соответствующий код на чистом Python будет выглядеть так:

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

Cython-эквивалент этой функции очень похож на её исходный вариант. Соответствующий код нужно поместить в файл с расширением .pyx. Единственное изменение, которое нужно внести в код, заключается в добавлении в него сведений о типах переменных и функции:

cpdef int test(int x): cdef int y = 1 cdef int i for i in range(1, x+1): y *= i return y

Обратите внимание на то, что перед функцией стоит ключевое слово cpdef. Это позволяет вызывать данную функцию из Python. Кроме того, тип назначен и переменной i, играющей роль счётчика цикла. Не будем забывать о том, что типизировать нужно все переменные, объявленные в функции. Это позволит компилятору C узнать о том, какие именно типы ему использовать.

Теперь создадим файл setup.py, который поможет нам преобразовать Cython-код в C-код:

from distutils.core import setup
from Cython.Build import cythonize setup(ext_modules = cythonize('run_cython.pyx'))

Выполним компиляцию:

python setup.py build_ext --inplace

Теперь С-код готов к использованию.

Если вам интересно — откройте этот файл и посмотрите на то, какой С-код сгенерировал Cython. Если взглянуть в папку, в которой находится Cython-код, там можно будет найти все файлы, необходимые для запуска C-кода, включая файл run_cython.c.

Ниже приведён код, используемый для тестирования и сравнения двух вариантов программы. Теперь всё готово к тестированию нашего сверхбыстрого C-кода.

import run_python
import run_cython
import time number = 10 start = time.time()
run_python.test(number)
end = time.time() py_time = end - start
print("Python time = ".format(py_time)) start = time.time()
run_cython.test(number)
end = time.time() cy_time = end - start
print("Cython time = {}".format(cy_time)) print("Speedup = {}".format(py_time / cy_time))

Код этот устроен очень просто. Мы импортируем необходимые файлы — так же, как импортируются обычные Python-файлы, после чего вызываем соответствующие функции, делая это так же, как если бы мы всё время работали бы с обычными Python-функциями.

Можно заметить, что Cython-версия программы оказывается быстрей её Python-версии во всех случаях. Взгляните на следующую таблицу. Чем масштабнее задача — тем больше и ускорение, которое даёт использование Cython.

Итоги

Использование Cython позволяет значительно ускорить практически любой код, написанный на Python, не прилагая к этому особенных усилий. Чем больше в программе циклов и чем больше данных она обрабатывает — тем лучших результатов можно ждать от применения Cython.

Уважаемые читатели! Используете ли вы Cython в своих проектах?

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть