Назад к блогу

JIT в CPython отозван: что именно удаляют из main и что остаётся

JIT в CPython отозван: что именно удаляют из main и что остаётся

Отзыв PEP 836 означает, что экспериментальный JIT-компилятор CPython покидает основную ветку разработки — из main удаляют runtime-часть и инструменты сборки, тогда как Tier 2 оптимизатор сохраняется. Разбираем, как был устроен JIT, что именно исчезает из репозитория и почему управляющий совет счёл предложенные гарантии недостаточными.

19 октября Savannah Ostrowski объявила об отзыве PEP 836 — документа, который должен был превратить экспериментальный JIT-компилятор CPython в поддерживаемую часть интерпретатора. PEP 836 должен был представить JIT как поддерживаемую, неэкспериментальную часть CPython: его гарантии, обязательства по поддержке и влияние на распространителей. Работа над JIT в основном репозитории прекращается; код, добавленный в 2024 году для Python 3.13, вероятно, будет удалён из main в ближайшее время — чтобы проблемы от удаления успели устранить в начале цикла разработки Python 3.16. Продолжение работы вне main остаётся на усмотрение сообщества.

Что именно удаляют из main

Из main уходят две части: файл Python/jit.c — вся runtime-часть JIT-компилятора — и каталог Tools/jit, где лежали инструкции по сборке JIT и код компиляции и разбора шаблонов на этапе сборки. Весь код в jit.c обёрнут в условие компиляции _Py_JIT: это не отдельный объект удаления, а флаг, под которым существует JIT.

Tier 2 оптимизатор остаётся. Файл optimizer.c включается отдельным флагом _Py_TIER2 и от _Py_JIT не зависит, хотя и подключает заголовок pycore_jit.h.

Как был устроен JIT

JIT компилировал executor на месте. Сначала одним проходом по трассе суммировался размер кода и данных, а также собирались маски нужных символов; по маске вычислялся размер trampoline. Вторым проходом для каждой инструкции порождался соответствующий ей машинный код по шаблону данной микро-операции; в конец данных добавлялась защитная инструкция от переполнения буфера.

На x86_64, когда цель оказывалась вне диапазона 32-битного смещения, генерировался 14-байтный трамплин (переход по 64-битному адресу), и место вызова патчилось на него. Память сначала выделялась и заполнялась, затем помечалась исполняемой (через mprotect или VirtualProtect) со сбросом i-cache; результат сохранялся в полях jit_code и jit_size исполнителя, а освобождала память отдельная функция.

JIT надстраивался над специализирующим адаптивным интерпретатором. Специализация ограничена границами отдельных инструкций, но собирает профиль — типы, раскладку памяти, горячие пути, — из которого строятся трассы микро-опов. Оптимизатор создаёт из них исполнители, кладёт их в массив co_executors кода и заменяет инструкцию на ENTER_EXECUTOR с индексом исполнителя: эта инструкция при исполнении передаёт управление заранее подготовленному исполнителю по его номеру в массиве, минуя обычную диспетчеризацию байткода. Эта машинерия включена во все сборки с Python 3.13, но отключена по умолчанию: накладные расходы интерпретации даже оптимизированных трасс слишком велики для большинства кода. JIT снимал их статической компиляцией — убирал диспетчеризацию между микро-опами, декодирование отдельных операций и трафик памяти.

Почему это сделали

В июне совет по управлению Python потребовал standards-track PEP, обосновывающий JIT «как поддерживаемую, неэкспериментальную часть CPython: его гарантии, обязательства по поддержке и влияние на редистрибьюторов». Срок — шесть месяцев на подачу и принятие; иначе код JIT должен быть удалён из main. Совет ждал до после релиза бета-версии Python 3.15, чтобы подтвердить статус-кво для 3.15, и не называл единого триггерного события — речь шла о том, чтобы JIT не мешал другим проектам: инструментации, отладке, профилированию, внешним JIT вроде CinderX, Numba, PyTorch compile.

PEP 836 подготовили Bucher, Ostrowski и Ken Jin и опубликовали 3 июля. Совет нашёл его «слишком оптимистичным» насчёт видимых пользователю различий при включённом JIT и спросил, считать ли такие различия багами и что делать, если различие в поведении неизбежно. Безопасность совет назвал недостаточно охваченной областью и предложил обсудить её с командой реагирования на инциденты безопасности Python. Срок проверки совместимости — прогона тестовых наборов top PyPI пакетов, изначально привязанного к первому release candidate Python 3.17 (около августа 2028) — совет потребовал сдвинуть на непрерывный процесс, начинающийся как можно скорее. Отдельное беспокойство: проектные решения по JIT принимаются без критического обзора со стороны более широкой группы разработчиков, отвечающих за разработку, поддержку и сопровождение CPython.

В отзыве Ostrowski объяснила: совет справедливо ставит высокую планку для предложения такого масштаба, а у авторов нет личного запаса сил, чтобы продвигать текущую версию дальше. Техническое направление — включая поддержку free-threaded режима и фронтенд на основе методов — авторы по-прежнему считают верным. Референсную реализацию обещают сделать доступной по ссылке из отозванного PEP. Brett Cannon уточнил, что альтернативный PEP до дедлайна совета маловероятен: все активно двигавшие JIT люди были авторами или контрибьюторами этого PEP.

Что меняет для сборки и дистрибутивов

Сборка JIT была опциональной: без неё JIT можно было просто игнорировать, иначе требовалось установить совместимую версию LLVM и передать соответствующий флаг сборочным скриптам. LLVM использовался только на этапе сборки — компилировал отдельные микро-операции в блоки машинного кода, которые линковались в шаблоны JIT, применяемые при сборке CPython; runtime-зависимости от LLVM не было, и конечным пользователям она не выставлялась. Сборка JIT добавляла от 3 до 60 секунд в зависимости от платформы и пересобиралась только при устаревании сгенерированных файлов.

Требовался именно Clang: это единственный C-компилятор с поддержкой гарантированных хвостовых вызовов (musttail), на которых держался continuation-passing-style. Без них хвостовые вызовы между шаблонами могли привести к неограниченному росту C-стека и переполнению. Целевая платформа задавалась triple. Для Apple Silicon macOS-релизы, по-видимому, должны были включать JIT Entitlement для Hardened Runtime — это не усложняло установку Python, но добавляло шаги release-менеджерам.

JIT не должен был распространяться конечным пользователям и использоваться в продакшене, пока остаётся экспериментальным, — и в main он был выключен по умолчанию.

Что известно о производительности

Единственный числовой ориентир — критерий не-экспериментального статуса: заметное улучшение хотя бы на одной популярной платформе, реалистично порядка 5%. В открытых вопросах PEP 744 указано, что JIT примерно так же быстр, как существующий специализирующий интерпретатор на большинстве платформ.

Про специализацию PEP 659 говорит, что основная часть ускорения идёт именно от неё — крупнейшие вклады дают поиск атрибутов, глобальные переменные и вызовы, — а малая, но полезная доля от улучшенной диспетчеризации: супер-инструкций и прочих оптимизаций, включаемых quickening. Про объёмы регрессий данных нет: регрессии в релизах main описаны качественно, как относительно частая жалоба на Python.

По памяти JIT расходовал больше базового интерпретатора, потому что выделял дополнительную память под исполняемый машинный код: по официальным бенчмаркам примерно на 10–20% больше. Верхняя граница диапазона обусловлена aarch64-apple-darwin с большими размерами страниц и, значит, большей минимальной гранулярностью выделения. Сами авторы просят относиться к цифрам с осторожностью: у бенчмарков не очень высокая базовая нагрузка по памяти.

Обратная совместимость

Удаление JIT не считается обратно несовместимым изменением: CPython всегда можно собрать без JIT, поэтому и удаление поддержки JIT для платформы таковым не признаётся. Поведение Python-кода не меняется — интерпретатор и JIT-бэкенд генерируются из одной спецификации. Наблюдаемые различия, которые находили и исправляли при тестировании, по PEP 744 обычно оказывались багами существующих стадий перевода в микро-опы и оптимизации, а не шага copy-and-patch.

Открытые вопросы

Судьба Tier 2 оптимизатора в явном виде не обсуждается: он остаётся, поскольку не зависит от флага JIT. Неясно, намерены ли исходные драйверы JIT продолжать работу вообще или всё будет оставлено другим при достаточном интересе сообщества; ответа на запрос автору PEP на момент публикации не поступило. В открытых вопросах PEP 744 помимо скорости указана память.

Источники

Похожее