Время прочтения: 6 мин.

Итак, что такое Cython?

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

def is_prime_python(n):
  for i in range(2, n):
    if n % i == 0:
      return False
  return True
%%time
_ = is_prime_python(6_700_417)

Output:

CPU times: user 577 ms, sys: 4.35 ms, total: 582 ms
Wall time: 584 ms

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

Теперь рассмотрим визави – Cython и реализацию его функции проверки числа:

%%cython

def is_prime_cython(int n):
  cdef int i
  for i in range(2, n):
    if n % i == 0:
      return False
  return True
%%time
_ = is_prime_cython(6_700_417)

Output:

CPU times: user 23.1 ms, sys: 0 ns, total: 23.1 ms
Wall time: 26.8 ms

На первый взгляд отличий в коде практически нет, однако по результатам производительности видно превосходство Cython почти в 25 раз. Давайте разберемся в чем же дело.

Разница состоит в том, что Cython в отличие от Python статически компилируемый и в нем есть, скажем, синтаксический сахар, который позволяет добавить статическую информацию о типах. В примере реализации функции на Python (напомню, что данный язык динамически типизируемый) в качестве аргумента подается какое-то n, а это n может оказаться чем угодно. И из-за, казалось бы, такой мелочи Python очень медленный. Интерпретатор Python совершает очень много действий для того, чтобы выяснить что же находится в этой переменной n.

В Cython, как и в таких языках как Java, C++, явно указывается то, что переменная n имеет тип int и переменная в цикле i также имеет тип int. Из-за этих явных указаний типов в переменной и происходит ускорение кода, так как больше нужно думать о том, какой это объект, какой у него тип данных и т.д.

На рисунке 1 представлена краткая схема пайплайна Cython, где видно, что Cython код транслируется в C/C++ код, а далее компилируется в питоновское расширение .pyd, которое потом уже может использоваться с помощью Python. Это тоже одна из причин почему Cython супербыстрый.

Возникает хороший вопрос: «как компилировать Cython?». А на самом деле компилировать его очень даже просто. В примере реализации функции на Cython вызывается команда %%Cython, которая в свою очередь компилирует Cython код в ячейке. Как мы знаем в С/С++ необходимо использовать Visual Studio, писать, запускать, компилировать, поэтому всё это бывает довольно сложно и вопрос, который возникает в случае с компилируемым языком «насколько сложно его компилировать?». В случае с Cython – супер просто. Условно в Jupyter ноутбуке написали код, вызвали команду %%Cython и код скомпилирован – его можно использовать. Если же имеется какой-то Cython код в файле, то необходимо просто импортировать install(), вызвать его. Соответственно, когда импортируются какие-то функции из файла, то Cython автоматически скомпилирует этот код.

Код, написанный в редакторе:

def is_prime_cython(int n):
  cdef int i
  for i in range(2, n):
    if n % i == 0:
      return False
  return True

Теперь импортируем его следующей командой:

from pyximport import install
install()
from prime_check import is_prime_cython

Подведем промежуточные итоги:

  • Cython — компилируемый язык;
  • Его синтаксис очень похож на Python, но с некоторыми дополнениями для статической типизации;
  • Статическая типизация + компиляция = скорость;
  • Писать только Python код, в случае Cython, совершенно нормально;
  • Нет никаких проблем с компиляцией.

Знатоки Numba скажут: зачем смотреть в сторону Cython, если можно просто использовать декоратор из Numba и код существенно прибавит в быстродействии.

Но поговорим немного о разнице. Напишем то же самый код для проверки числа только теперь сравним все полученные результаты:

PythonCythonNumba
def is_prime_python(n):  
for i in range(2, n):
    if n % i == 0:
      return False
  return True
%%cython
  def is_prime_cython(int
n):
  cdef int i
  for i in range(2, n):
    if n % i == 0:
      return False
  return True
@numba.njit
def is_prime_python(n):
  for i in range(2, n):
    if n % i == 0:
      return False
  return True
%%time
_ =
is_prime_python(6_700_4
17)
%%time
_ =
is_prime_cython(6_700_4
17)
%%time
_ =
is_prime_python(6_700_4
17)
CPU times: user 753 ms,
sys: 3.75 ms, total:
756 ms
Wall time: 783 ms
CPU times: user 21.3
ms, sys: 139 µs, total:
21.5 ms
Wall time: 21.5 ms
CPU times: user 113 ms,
sys: 1.18 ms, total:
114 ms
Wall time: 115 ms

Видно, что, добавив декоратор @numba.njit, код ускорился почти в 5 раз и нет необходимости добавлять что-то лишнего, как в случае Cython. И зачем тогда изучать Cython, если везде можно обходиться одними декораторами от Numba?

У Cython есть одно большое преимущество перед Numba. Поскольку Cython язык, в нем есть всякие вещи по типу классов, в то время как Numba просто компилятор (не язык). Поэтому Cython может быть объектно-ориентированным, а Numba не может работать с классами нормально.

Также кратко отметим и другие плюсы Cython:

  • Cython поддерживает многомерные массивы (включая Numpy), следовательно более быстрый доступ к данным;
  • Cython выполняет множество проверок за вас: проверка границ массива, деление на ноль, переполнение;
  • Cython не ограничивается численными вычислениями.

Однако можно задаться вопросом: «зачем нам такой Python, если Cython это по сути тот же Python только быстрее и вообще он идеальный». Тем не менее и в Cython есть свои проблемы. Порой Cython код сложно отлаживать: когда допускается одна ошибка, синтаксический анализатор Cython проходит по всему коду, как лавина, и после одной ошибки возникает миллион других ошибок. Поэтому совет – смотрите самый верх (первые строчки) в сообщении об ошибке.

Документация не самая лучшая, так как это своего рода набор туториалов, то есть азы изучить можно, но если необходимо углубится в какой-то функционал, то трудно найти или практически невозможно найти описание.

Когда пишется оптимизированный код на Cython, он начинает походить на C, а C без соответствующей культуры начинает превращаться в какую-то кашу.

Просуммируем полученные знания, а именно:

  • Cython быстрый как C;
  • Поскольку это язык, то он очень гибкий;
  • Легко оборачивать C/C++ код;
  • Отладка Cython кода может быть проблемой;
  • Не лучшая документация.

Существующие проекты, которые используют Cython показаны на рисунке 4, подтверждают тот тезис, что Cython создан не только для каких-то математических задач. Так Quora – веб-сайт, на котором отвечают на всякие вопросы, имеет очень загруженный траффик. Чтобы избавиться от проблем, которые преследуют в Python, этот сайт решили написать на Cython. К тому же уже знакомые библиотеки Pandas, а также Scikit-Learn используют язык Cython для улучшения своего функционала.

Подведем финальные итоги по всему вышесказанному:

  • Cython – это Python с некоторыми статическими вещами для скорости;
  • Cython отлично подходит для ускорения приложений Python;
  • Упакован множеством замечательных функций: оборачивание C/C++ кода, параллельные вычисления;
  • Не ограничивается численными вычислениями.