17.09.2021

Facebook открыл код Cinder, форка CPython, используемого в Instagram

Компания Facebook опубликовала исходные тексты проекта Cinder, развивающего ответвление от CPython 3.8.5, основной эталонной реализации языка программирования Python. Cinder применяется в рабочей инфраструктуре Facebook для обеспечения функционирования сервиса Instagram и включает оптимизации для повышения производительности.

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

Код Cinder отмечается как достаточно надёжный и проверенный в рабочих окружениях, но в случае выявления проблем их придётся решать самостоятельно, так как Facebook не гарантирует, что будет реагировать на внешние сообщения об ошибках и pull-запросы. При этом Fecebook не исключает конструктивное сотрудничество с сообществом и готов обсудить идеи как сделать Cinder ещё быстрее или как ускорить перенос подготовленных изменений в основной состав CPython.

Основные оптимизации, реализованные в Cinder:

  • Inline-кэширование байткода («shadow bytecode»). Суть метода в выявлении ситуаций выполнения типового опкода, которые можно опитимизировать, и динамической замене подобного опкода на более быстрые специализированные варианты (например, замена часто вызываемых функций).
  • Активное вычисление сопрограмм (Eager coroutine evaluation). Для вызовов async-функций, которые сразу обрабатываются (await не приводит к ожиданию и функция достигает оператора return раньше), результат подобных функций напрямую подставляется без создания сопрограммы и без привлечения цикла обработки событий. В применяемом в Facebook коде, в котором активно используется async/await, оптимизация приводит к ускорению примерно на 5%.
  • Выборочная JIT-компиляция на уровне отдельных методов и функций (method-at-a-time). Включается через опцию «-X jit» или переменную окружения PYTHONJIT=1 и позволяет ускорить выполнение многих тестов производительности в 1.5-4 раза. Так как JIT-компиляция актуальна только для часто выполняемых функций, нецелесообразно применять её для редко используемых функций, накладные расходы на компиляцию которых могут лишь замедлить выполнение программы.

    Через опцию «-X jit-list-file=/path/to/jitlist.txt» или переменную окружения «PYTHONJITLISTFILE=/path/to/jitlist.txt» можно указать файл со списком функций, для которых можно использовать JIT (формат path.to.module:funcname или path.to.module:ClassName.method_name). Список функций для которых следует включить JIT можно определить на основе результатов профилирования. В будущем ожидается поддержка динамической JIT-компиляции на основе внутреннего анализа частоты вызова функций, но с учётом специфики запуска процессов в Instagram, для Facebook подходит и JIT-компиляция на начальном этапе.

    JIT вначале преобразует байткод Python в высокоуровневное промежуточное представление (HIR), которое достаточно близко к байткоду Python, но рассчитано на использование регистровой виртуальной машины, вместо стековой, а также использует информацию о типах и дополнительные детали, важные для производительности (например, подсчёт ссылок). HIR затем преобразуется в форму SSA (static single assignment) и проходит стадии оптимизации, учитывающие результаты подсчёта ссылок и данные о потреблении памяти. В итоге генерируется низкоуровневное промежуточное представление (LIR), близкое к языку ассемблер. После ещё одной фазы оптимизаций на основе LIR при помощи библиотеки asmjit генерируются ассемблерные инструкции.

  • Режим strict для модулей. Функциональность включает три компонента : Тип StrictModule. Статический анализатор, способный определить, что выполнение модуля не оказывает влияние на код за пределами этого модуля. Загрузчик модулей, определяющий, что модули переведены в режим strict (в коде указывается «import __strict__»), проверяющий отсутствие пересечений с другими модулями и загружающий strict-модули в sys.modules в виде объекта StrictModule.
  • Static Python — экспериментальный компилятор байткода, использующий аннотации типов для генерации байткода, специфичного для каждого типа и выполняемого быстрее благодаря применению JIT-компиляции. В некоторых тестах сочетание Static Python и JIT демонстрирует повышение производительно до 7 раз, по сравнению с типовым CPython. Во многих ситуациях результаты оцениваются как близкие к применению компиляторов MyPyC и Cython.

Источник.