«Любовь. Питон. C++». Отчет Яндекса

Какая связь между Python и C++? Какую пользу вы можете извлечь из этого для себя? На большой конференции Pytup Александр Букин показал способы, с помощью которых можно оптимизировать свой код, а также выбирать и эффективно использовать сторонние библиотеки.

— Всем привет, меня зовут Александр Букин, я разрабатываю Яндекс.

Погоду.

Возможно, вы также знаете меня как соучредителя Pytup. Еще я вхожу в программные комитеты таких крутых конференций, как PyCon.ru и YaTalks. Сегодня мы поговорим о любви к Python и C++.

Точнее, о том, что их объединяет, эти два прекрасных языка и, собственно, C, и чему мы можем научиться из этих замечательных взаимоотношений.

Дисклеймер: название репортажа является отсылкой к замечательному сериалу «Любовь.

Смерть.

Роботы» на Netflix. Всем очень рекомендую.

Чтобы понять сложное переплетение двух языков, нам придется вернуться в прошлое.

Давным-давно в не очень далекой от нас галактике родился герой.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Этот герой был силен, он владел силой далеко не по годам.

У него было много мидихлорианов.

И все знали, что этот парень очень быстр.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Он любил скорость.

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

Он зарос библиотеками.

Его сила росла.

Но в какой-то момент, не очень далекий, он понял, что хочет большего, что он стоит на месте, что ему нужно двигаться.

И для этого он решил испытать другую сторону, испытать на себе мощь ООП и улучшенных библиотечных стандартов.

Осознав эту силу, он возродился и стал сильнее, и мы все узнали в нем С++.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Но незадолго до этого ему удалось родить нового чудесного героя, нашего любимого Питона.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Как мы знаем, их связь — C и Python — прямая.

И Пифон на некоторое время был разлучен со своим отцом.

Развивался он немного отдельно, хотя отец, конечно, имел на него большое влияние.

И не все у них складывалось хорошо.

Иногда ему было даже трудно принять, что C++ — его отец.

Он не всегда хорошо с этим справлялся.

Прошло время, а ему все же удалось найти общий язык с отцом.

Объединив усилия и обретя эту любовь, Python и C++ вместе победили зло во вселенной.

Их объединенная сила была настолько велика, что никто не мог их сдержать.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Я думаю, это замечательно.

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

Как я уже говорил, C++ всегда был очень быстрым, а вот Python — не всегда.

Скорее, он даже прославился тем, что у него иногда страдает производительность на важных задачах.

Именно в этом может помочь C++ — ускорить ваш код, некоторые части, которые вы очень часто вызываете в своих сервисах.

Давайте узнаем, как это сделать.

Во-первых, отказ от ответственности: никакой C++ не ускорит ваш код лучше, чем вы.

Прежде чем вы попробуете методы, которые мы обсудим далее, вы всегда должны помнить, что мы начинаем с написанного нами кода.

Скорее всего, его можно оптимизировать прямо сейчас, просто пройдя через это.

И прежде чем пробовать C-хаки, просто попробуйте улучшить алгоритм, очистить текущий код Python. Но если вы уже это сделали или у вас просто сильный дух приключений, то я приглашаю вас в чудесный мир ускорения Python.

«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

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

Сегодня мы рассмотрим два из них — Cython и Native Extension in C/C++.

Давайте подробнее рассмотрим нативные расширения.

Да, там есть еще одна подгруппа.

Но, во-первых, эти двое — мои любимые.

Во-вторых, чтобы рассказать о каждом из них, недостаточно целого отчета.

Краткое введение о Cython. Это достаточно популярная технология: с ее помощью написан, например, gevent. Он легко интегрируется в сборку проекта, поэтому мне он нравится.

У Cython довольно хорошая документация.

Еще есть полезная штука для разработчика — cythonize. Это обертка вашей функции, вызывающая эту функцию, которая в среднем в синтетических тестах даже ускоряет выполнение кода до двух раз.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Ссылка со слайда Понятно, что есть и недостатки.

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

И иногда это мешает. Во-вторых, когда вам все-таки придется переключиться на C, вам нужно это знать.

Но здесь, кстати, не всегда обязательно это знать.

Конечно, есть наш любимый Segmentation Fault, который можно поймать, если плохо работать с памятью уже в сыром C. Это тоже приводит к трудностям при отладке.

Но если вы не хотите слишком глубоко погружаться в C и хотите попытаться ускорить свою технологию прямо сейчас, Cython — хороший выбор.

Но мы, как говорится, верим в абсолют, поэтому абсолютная сила – это абсолютная скорость.

И мы хотим значительно ускорить выполнение, поэтому давайте зайдём внутрь.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Ссылка со слайда Чтобы попасть внутрь, достаточно использовать собственное расширение.

Хоть это и не самая простая технология, но она родная.

Его документация находится прямо в документации Python. Он также легко интегрируется в сборку проекта и позволяет создавать собственные типы данных.

Здесь уже нельзя обойти необходимость знать C и писать на нем код. Здесь, если делать что-то сложное, придется работать с памятью и отлаживать ее.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Небольшой пример.

Вот минимальный файл spammodule.c, который описывает, как ни странно, спам-модуль.

Как мы видим, мы включаем заголовочный файл include Python.h, который нам понадобится для любого модуля.

И опишем наш модуль.

Говорим, какое у него будет имя, и описываем функцию его инициализации PyInit_spam. Затем мы вызываем PyModule_Create, который либо возвращает значение null, либо возвращает модуль.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Чтобы все это работало, нам еще нужно собрать наш модуль.

Для этого вы можете использовать setuptools. Пишем небольшой setup.py, в котором указываем, что нам нужно Расширение.

Рассказываем, как это назвать и откуда взять исходники.

Запустите сборку setup.py, установку setup.py. Вы можете импортировать его, вы можете использовать его.

Это пример для C. Пример для C++ выглядит очень похоже.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Мы просто пишем исходный код с плюсом, добавляем исходники spammodule.cpp и указываем, что наш язык тоже C++.

Поздравляю, вы прекрасны.

Вам просто нужно написать в разделе вашего .

c или .

cpp файла валидный, хорошо работающий код на C и плюсах, который правильно интерпретирует управление памятью.

Возможно, вы не совсем готовы к этому.

Может быть, вам просто лень это делать, или вы думаете: я разработчик, а разработчик не делает свои велосипеды.

Наверное, раз это такой знакомый и давно используемый механизм, то уже есть кто-то, кто это сделал.

И да, уже есть.

Давайте посмотрим на пару примеров.

Например, есть ujson.

«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Что мы делаем часто, как и все разработчики? Перекладываем фишки Джейсона.

Эта библиотека довольно проста в использовании.

Он имеет стандартные функции дампа и загрузки.

А внутренне это реализовано с помощью Extension. Сверху есть пара файлов C и обертка.

Давайте не будем верить мне на слово, что простое добавление кода C ускорит работу с файлами.

Давайте проверим его, сериализуем и десериализуем.

Все измерения, которые я покажу дальше, указаны в миллисекундах.

Для начала возьмем не очень большой файл, который возвращает Twitter API, JSON, размером 600 килобайт.

«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Ссылка со слайда За основу я взял две очень популярные библиотеки Python: json и simplejson. Мы получаем, что сериализация и десериализация занимают около 3,5 миллисекунд для json и 3,15 для simplejson. Выглядит довольно быстро.

Как вы думаете, сколько времени понадобится ujson, чтобы это сделать? Допустим, он делает это ровно за 3 секунды.

Возможно для 2.9. Но на самом деле прирост будет больше.

Я добавил ссылку на тест, и сериализация заняла почти 2 миллисекунды.

Как видим, увеличение в полтора раза – это вполне неплохо.

Но, конечно, хотелось бы большего.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Ссылка со слайда Возьмем файл побольше — canada.json. Это файл размером 2 мегабайта с геоточками.

Мы видим, что тот же simplejson уже не так четко работает; сериализация заняла целых 80 миллисекунд. json немного лучше.

Но здесь ujson гораздо сильнее вырывается вперёд на большом объёме данных, и мы уже получаем прирост в четыре с половиной раза относительно simplejson с сериализацией и в три раза относительно json. Отличный результат. Я позволю тебе насладиться этой скоростью.

Но есть и ложка дегтя.

Понятно, что библиотека ujson не во всем хороша с точки зрения совместимости.

Он поддерживает не все типы данных, которые можно сериализовать.

Если вы найдете его на GitHub, то увидите, что там довольно много проблем, иногда есть утечки памяти, иногда есть ошибки.

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

Но если у вас где-то сериализован json и ограничения этой библиотеки вас устраивают, попробуйте.

Вы можете быть приятно удивлены.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Давайте посмотрим на задачу, которая возникает часто и которая исторически не выполнялась в Python очень быстро: анализ даты.

Есть такая библиотека — ciso8601. Он также написан с использованием расширения C. Вот как выглядит его использование.

Довольно просто.

Существует функция parse_datetime, в которой вы передаете строку в одном из поддерживаемых форматов.

Это анализируется в стандартный объект datetime. Он даже поддерживает часовые пояса.

Давайте тоже оценим.

Мы разберем следующую строку: 2014-01-09T21:48:00. Все измерения, которые мы получаем, будут в микросекундах.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Здесь добавлена еще одна версия Python. Будет интересно посмотреть, как это работает в разных версиях.

Я основал его на Python-dateutil, который по сути является расширенной стандартной библиотекой даты и времени, и популярной, но написанной на Python, str2date. python-dateutil в Python 3.8 делает это за 122 микросекунды — посмотрите, что необычно, это немного медленнее, чем 3.7. Гораздо быстрее, на порядок, это делает str2date. Что может предложить нам ciso? Вероятно, это будет одна микросекунда.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Ссылка со слайда На самом деле это будет меньше одной микросекунды, очень быстро.

И даже в одном из худших случаев с версией 3.8 это в 600 раз быстрее, чем исходный парсер datetime. Если взять версию 2.7, то она будет почти в 1000 раз быстрее.

Это уже гораздо более впечатляюще, чем наш предыдущий тест json. И мы задаемся вопросом: должно быть что-то не так, что-то в datetime не работает. Не совсем.

Просто у этой библиотеки есть и свои недостатки в плане поддержки форматов и отладки, если что-то пойдет не так.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Но давайте посмотрим на него повнимательнее, как на хороший пример расширения C. Вот небольшой проект. Файлов много, но самый важный — это Module.c. Этот файл содержит весь код этой библиотеки.

Это всего лишь один файл, и в нем всего 586 строк.

Второй важный файл — setup.py. Помните, сначала мы рассмотрели spammodule.c. Это точно такая же схема.

Существует один файл C и один файл setup.py. Как мы видим, внутри оно, конечно, более расширенное, но эта строчка — «Пожалуйста, дайте мне расширение, назовите его так, возьмите оттуда исходный код» — здесь тоже присутствует.

«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Ребята молодцы, что не усложняют. Помогает. Вот мы и затронули то, что, как говорится, можно сделать одной левой рукой.

Теперь вы можете просто изменить пару строк в своем коде, где вы работаете с датами с помощью json, и получить прибавку.

Я думаю, это замечательно.

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

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

Немного о том, где еще можно узнать, что такое расширения Python, чем они хороши и как его ускорить.

В 2018 году у нас на Pytup говорил Костя Гуков, рассказавший о расширении на Rust. Там он показал расширения, которые также анализируют даты.

Забегая вперед, скажу, что они медленнее написанных на C, но и очень быстрые.

Антон Патрушев также рассказал о расширении Rust на PyCon: Смотреть видео Как видите, сейчас это довольно популярно.

И если вдруг вам не нравится C, но вы хотите написать расширение, вы можете посмотреть в этом направлении.

Позже Кирилл расскажет, как ускорить не только ваш код или функцию, но и весь Python. И будет ли он ускоряться в принципе в ближайшее время? Советую всем послушать.

Подведем краткий итог:

  • Не забудьте оптимизировать свой код Python. Это первое, что нужно сделать, когда кажется, что что-то может произойти быстрее.

  • Если вы оптимизировали, попробуйте сторонние библиотеки, содержащие вещи, написанные с использованием расширений C и C++ или Rust. Возвращаясь немного назад, можно сказать, что ujson — не самая быстрая библиотека; есть более быстрые: orjson, njson. Попробовать их.

  • А если ваша задача достаточно узкая или вы хотите сделать что-то свое, пишите свои расширения, изучайте новые языки для этих расширений.

    Развивайтесь.

Да пребудет с вами Сила, друзья.



«Любовь.
</p><p>
 Питон.
</p><p>
 C++».
</p><p>
 отчет яндекса

Теги: #python #оптимизация сервера #C++ #Ненормальное программирование #Cython #оптимизация кода

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.