JIT-компиляция в PHP 8 — тема с громкими обещаниями, но и с массой нюансов. На презентациях — красивые графики, в бенчмарках — кратные ускорения. В реальном мире веб-приложений всё сложнее: сеть, диски, базы данных, шаблоны, кэширование и неизбежные вызовы C-расширений сильно «разбавляют» эффект от JIT. В этой статье собрал практический взгляд: где JIT действительно помогает, как корректно его мерить, что профилировать и какие ограничения учитывать перед включением в продакшене.
Что такое JIT в PHP 8 и как он работает
Классическая модель PHP — интерпретация опкодов Zend VM, которую ускоряет OPCache за счёт кэширования скомпилированных байткодов. JIT добавляет следующий уровень: горячие участки чистого PHP-кода компилируются в машинные инструкции и исполняются напрямую, минуя часть накладных расходов виртуальной машины. Цель — ускорить CPU-связанные участки, где именно интерпретация становится бутылочным горлышком.
Важно понимать несколько моментов:
- JIT ускоряет только чистый PHP-код. Вызовы функций из C-расширений (например, JSON, mbstring, PDO, GD/Imagick, OpenSSL) JIT не ускорит: там уже работает нативный код.
- Для веб-нагрузки часто доминируют I/O-операции: сетевые запросы к БД, файловая система, HTTP-запросы к внешним сервисам. В таких сценариях выигрыш от JIT ограничен или незаметен.
- JIT может существенно ускорить алгоритмические задачи на чистом PHP: обработку массивов, численные расчёты, парсеры, сериализацию/десериализацию пользовательских структур, итеративные преобразования данных.
Real world: чего ждать в продакшене
Типичный стек современного веба на PHP — это фреймворк, ORM/клиент БД, шаблоны/рендер, кэш, очереди, RPC/HTTP-клиенты. Львиную долю времени запросы проводят в ожидании I/O либо внутри C-расширений. В таком «среднем» сценарии JIT редко даёт кратный прирост. Чаще это 0–10% к throughput и/или небольшое снижение CPU под стабильной нагрузкой.
Где потенциал заметней:
- CLI-процессы и воркеры очередей, выполняющие длительные вычисления на чистом PHP (парсинг, генерация отчетов, конвертация доменных моделей, агрегации, нетривиальные трансформации данных).
- Сервисы с высокой долей CPU-bound логики, минимальным количеством внешних запросов и хорошо прогретым кэшем.
- Монолит с тяжёлыми middleware/валидаторами/сериализаторами, написанными на чистом PHP, где вызываемые горячие функции стабильно «нагреваются» в runtime.
Для воркеров очередей и фоновых задач пригодится разбор практики из статьи Supervisor, systemd и управление PHP-воркерами.
Если ваши p95/p99 латентности определяются очередями к БД, блокировками и сетевыми задержками — JIT вряд ли станет серебряной пулей. Сначала устраните I/O-узкие места и только затем ожидайте эффекта от JIT.

Методология замеров без самообмана
Большинство разочарований с JIT — из-за неверной методики. Нужна чистая A/B-схема, одинаковый код и данные, сопоставимые профили трафика и достаточно длительный период стабилизации.
Что мерить
- Пропускная способность (RPS/QPS) при фиксированном профиле запросов.
- Латентности p50/p90/p95/p99, отдельно холодный старт и прогретое состояние.
- CPU (user/system), context switches, run-queue. На Linux удобно смотреть агрегаты по php-fpm процессам.
- RSS/VSZ php-fpm воркеров, фрагментацию и плотность памяти.
- Показатели OPCache/JIT: размер и заполнение
opcache.jit_buffer_size, долю JITted-функций, причины деоптимизаций.
Контроль среды
- Одинаковые версии PHP 8.x и расширений, одинаковые build-флаги и параметры OPCache.
- Идентичные настройки php-fpm: количество воркеров, лимиты памяти, timeouts.
- Фиксированный warmup: прогрейте кэш/роуты/контейнеры DI до начала замеров.
- Нагрузочное тестирование с репрезентативным набором эндпоинтов и весами, приближёнными к production трафику.
Если нет изолированного стенда, проще всего быстро развернуть его на VDS, где вы полностью контролируете версии PHP, php-fpm и параметры OPCache/JIT.
Профилирование
Чтобы понять, где JIT может помочь, нужен профиль CPU. Подойдёт выборка стека в продакшене с низкой частотой (sampling), либо детальный профиль на staging. Избегайте тяжёлых пошаговых дебаггеров в продакшене: они искажают картину. Ищите в flamegraph «горячие» чисто-PHP функции, которые долго крутятся без внешних ожиданий — кандидаты для выигрыша от JIT.
Стратегия A/B и canary
Безопасный путь — включить JIT для части пула php-fpm и направить туда долю трафика. Сравните ключевые метрики и поведение GC/OPCache, оцените стабильность и память. Если всё хорошо — увеличивайте долю до 100%.
Настройки: как включать и контролировать
Базовые параметры включения JIT задаются в INI:
; php.ini или конфиги для php-fpm
opcache.enable=1
opcache.enable_cli=0
opcache.jit=tracing
opcache.jit_buffer_size=128M
; Дополнительно
opcache.validate_timestamps=1
opcache.max_accelerated_files=100000
opcache.memory_consumption=256
Комментарии к параметрам:
opcache.jit: режим tracing обычно даёт лучшие результаты для реальных приложений, но убедитесь в стабильности именно на вашей версии PHP.opcache.jit_buffer_size: буфер под машинный код; для средних проектов стартовая вилка 64–256 МБ. Если буфер заполняется, новые участки не JITтятся, полезно мониторить «full» события.opcache.enable_cli: имеет смысл включать (1) для CLI-скриптов/воркеров, если они CPU-bound и долгоживущие.
Тонкая настройка по пулам php-fpm позволяет включать JIT точечно:
; в конфигурации пула php-fpm
php_admin_value[opcache.jit]=tracing
php_admin_value[opcache.jit_buffer_size]=128M
Проверяйте статус программно, чтобы видеть реальную картину, а не только INI:
<?php
$st = opcache_get_status(false);
print_r($st['jit'] ?? []);
Следите за метриками JIT: количество откомпилированных функций, заполнение буфера, промахи и деоптимизации. При росте доли деоптимизаций эффект снижается.

Ожидаемые результаты: без магии
По совокупному опыту производственных нагрузок:
- Типичные веб-приложения с БД и кэшем: от 0 до +10% к RPS на стабильном трафике, незначительное снижение CPU на тот же RPS, латентности p95/p99 меняются в пределах погрешности.
- CPU-bound услуги/эндпоинты: ускорение 10–30% в «горячем» состоянии, иногда больше на отдельных алгоритмах, если они написаны на чистом PHP.
- CLI-воркеры: потенциал выше, особенно для длительных задач с большим количеством однотипных вычислений и минимальным I/O.
Издержки и побочки:
- Память: увеличение RSS php-fpm воркеров на десятки-сотни МБ на пул, в зависимости от буфера JIT и интенсивности компиляции.
- Холодный старт: пока код не прогрет, эффект ограничен. На коротких запросах, где запуск и завершение быстры, JIT может почти не успевать себя проявить.
- Фрагментация и заполнение буфера: при нехватке буфера часть горячих путей не JITтится, прирост тает.
JIT усиливает сильные стороны чистого PHP-кода, но не решает архитектурные проблемы. Если 70% времени уходит в БД, никакой JIT не компенсирует медленные запросы и неоптимальные индексы.
Ограничения и риски
Совместимость и стабильность
Хотя JIT в ветках 8.1+ стал значительно стабильнее по сравнению с 8.0, редкие краевые случаи всё ещё встречаются — особенно с нестандартными расширениями и агрессивными оптимизаторами. Обязательны нагрузочные тесты перед включением на 100% трафика и понятный план отката.
Диагностика и профилирование
JIT усложняет профилирование: flamegraph может «меняться» из-за нативного кода, стеки иногда короче, привычные сигнатуры пропадают. Для чистоты исследований держите сценарии с JIT on/off и сравнивайте оба профиля. При поиске редких багов имеет смысл временно отключать JIT.
Безопасность и политика системы
JIT требует выделения исполнимых страниц памяти. На системах с жёсткими политиками безопасности (модули мандатного контроля, жёсткие правила исполнения) это может быть ограничено, и JIT не будет работать либо будет работать деградированно. Проверьте логи безопасности при аномалиях и консультируйтесь с политиками инфраструктуры.
Различия по CPU и окружению
Генерация кода зависит от архитектуры CPU и набора инструкций. На разных машинах эффект может отличаться, а переносимые «магические» числа в настройках зачастую не работают одинаково. Тестируйте на целевом окружении, а не на ноутбуке разработчика. Дополнительно о практической производительности на ARM-чипах — в статье ARM VDS: опыт и метрики.
Взаимодействие с OPCache/Preload
Preload ускоряет загрузку кода и уменьшает накладные расходы интерпретатора, но сам по себе не является JIT. Он помогает быстрее выйти в прогретое состояние, где JIT проявится лучше, однако чудес не совершает. И наоборот, при частых деплоях с инвалидированием кэша вы будете чаще сталкиваться с «холодными» периодами.
План внедрения в продакшене
- Соберите профиль CPU и подтвердите наличие горячих участков чистого PHP-кода, не завязанных на I/O.
- Подготовьте staging с репрезентативными данными и трафиком. Зафиксируйте все версии и параметры окружения.
- Включите JIT на части пула php-fpm (canary) и направьте долю трафика. Мониторьте RPS, p95/p99, CPU, RSS, состояние OPCache/JIT.
- Оцените поведение в пиковые часы и в периоды деплоя, когда кэш инвалидируется.
- Проведите негативные сценарии: заполнение
opcache.jit_buffer_size, рост числа воркеров, высокие очереди, burst-нагрузка. - Зафиксируйте эффект, решите, где JIT активировать постоянно: весь фронт, отдельные пулы, только CLI-воркеры.
- Настройте наблюдаемость: экспорт статусов OPCache, алерты на полный буфер JIT и аномальный рост памяти.
Чек-лист принятия решения
- Профили показали значимые горячие участки чистого PHP (не C-расширения и не I/O)?
- У вас есть запас RAM под рост RSS и буфера JIT?
- Нагрузка достаточно стабильна, чтобы код успевал прогреваться?
- Готова canary-стратегия и наблюдаемость OPCache/JIT?
- Проведены тесты стабильности на целевой версии PHP 8.x?
Типичные вопросы
Стоит ли включать JIT «всем и сразу»?
Нет. Начинайте с профиля и точечных экспериментов. Если проект I/O-bound, эффекта может не быть вовсе. На CPU-bound участках JIT часто оправдан.
А что с короткими веб-запросами?
При коротких транзакциях выгода минимальна: JIT просто не успевает окупить свои накладные расходы. Важнее оптимизировать маршрутизацию, кэширование и запросы к БД.
Нужно ли включать JIT для CLI?
Если ваши CLI-задачи долгие и CPU-bound — да, это один из лучших кандидатов. Включите opcache.enable_cli=1 и оцените результат.
Можно ли ухудшить ситуацию JIT’ом?
Да: рост памяти, редкие деоптимизации и нестабильность отдельных расширений могут перевесить небольшой прирост RPS. Поэтому обязательны A/B-тесты, мониторинг и готовность к откату.
Итоги
JIT в PHP 8 — полезный инструмент, но узкоспециализированный в контексте типичного веб-продакшена. Он способен дать ощутимый прирост на чисто вычислительных участках и в длительных CLI-задачах, но почти не влияет на I/O-bound сценарии и работу C-расширений. Правильная методология замеров, профилирование «real world» нагрузки и осторожное поэтапное включение — ключ к трезвым ожиданиям и безопасной эксплуатации. Если у вас есть CPU-bound узкие места на чистом PHP — JIT стоит вашего времени. Если нет — сначала оптимизируйте архитектуру, кэш, запросы и только затем экспериментируйте с JIT.


