В ядро PHP 8 добавлен JIT-компилятор, который потенциально может значительно повысить производительность. Однако есть важные оговорки касательно его реального влияния на веб-приложения, поэтому я провёл несколько бенчмарков производительности JIT (все релевантные ссылки я также указал в сносках).
Я также хотел посвятить отдельный пост настройке JIT, так как здесь есть о чём поговорить.
Честно говоря, настройка JIT — один из самых запутанных способов конфигурирования расширений PHP, с которыми я сталкивался. К счастью, есть короткие псевдонимы конфигурации, облегчающие настройку. Тем не менее полезно досконально разобраться в параметрах JIT, так что приступим.
Прежде всего, JIT работает только при включённом opcache. В большинстве установок PHP он включён по умолчанию, но вам следует убедиться, что параметр opcache.enable установлен в 1 в вашем файле php.ini . Само включение JIT выполняется путем указания opcache.jit_buffer_size в php.ini .
Обратите внимание: если вы запускаете PHP из командной строки, вы можете передать эти параметры через флаг -d , а не добавлять их в php.ini :
php -dopcache.enable=1 -dopcache.jit_buffer_size=100M
Если эту директиву опустить, по умолчанию используется значение 0, и JIT не запустится. Если вы тестируете JIT в CLI-скрипте, для включения opcache вам понадобится использовать opcache.enable_cli :
php -dopcache.enable_cli=1 -dopcache.jit_buffer_size=100M
Разница между opcache.enable и opcache.enable_cli заключается в том, что первый параметр нужно использовать, например, при запуске встроенного сервера PHP. Если же вы запускаете CLI-скрипт, вам понадобится opcache.enable_cli .
Прежде чем продолжить, давайте убедимся, что JIT действительно работает. Создайте PHP-скрипт, доступный через браузер или CLI (в зависимости от того, где вы тестируете JIT), и посмотрите на вывод opcache_get_status() :
var_dump(opcache_get_status()['jit']);
Вывод должен выглядеть примерно так:
array:7 [ "enabled" => true "on" => true "kind" => 5 "opt_level" => 4 "opt_flags" => 6 "buffer_size" => 4080 "buffer_free" => 0 ]
Если enabled и on равны true, всё готово к работе!
Далее, существует несколько способов настройки JIT (и вот здесь начинается путаница с конфигурацией). Вы можете настроить, когда должен запускаться JIT, насколько сильно он должен пытаться оптимизировать код и т. д. Все эти параметры задаются с помощью одного (!) параметра конфигурации: opcache.jit . Это может выглядеть примерно так:
opcache.enable=1 opcache.jit=1255
И что же означает это число? В RFC расписано значение каждой цифры. Имейте в виду: это не битовая маска, каждая цифра просто обозначает отдельную опцию конфигурации. В RFC приведены следующие варианты:
O — Уровень оптимизации
0 | не использовать JIT
1 | минимальный JIT (вызов стандартных обработчиков VM)
2 | избирательное встраивание обработчиков VM
3 | оптимизированный JIT на основе статического вывода типов для отдельной функции
4 | оптимизированный JIT на основе статического вывода типов и дерева вызовов
5 | оптимизированный JIT на основе статического вывода типов и анализа внутренних процедур
T — Триггер JIT
0 | JIT-компиляция всех функций при первой загрузке скрипта
1 | JIT-компиляция функции при первом выполнении
2 | Профилирование при первом запросе и компиляция «горячих» функций при втором запросе
3 | Профилирование на лету и компиляция «горячих» функций
4 | Компиляция функций с тегом @jit в doc-комментариях
5 | Tracing JIT
R — Распределение регистров
0 | не выполнять распределение регистров
1 | использовать локальный алгоритм распределения регистров linear-scan
2 | использовать глобальный алгоритм распределения регистров linear-scan
C — Флаги оптимизации под конкретный ЦП
0 | нет
1 | включить генерацию инструкций AVX
Один небольшой подвох: в RFC эти параметры перечислены в обратном порядке, поэтому первая цифра представляет значение C , вторая — R и так далее. Почему нельзя было просто добавить четыре отдельных параметра конфигурации — выше моего понимания, вероятно, чтобы ускорить настройку JIT… правда?
Как бы то ни было, разработчики ядра предлагают 1255 в качестве лучшего значения по умолчанию: это обеспечивает максимальный JIT, использует tracing JIT, глобальный распределитель регистров linear-scan (что бы это ни значало) и включает генерацию инструкций AVX.
Так что ваши настройки ini (или флаги -d ) должны содержать следующие значения:
opcache.enable=1 opcache.jit_buffer_size=100M opcache.jit=1255
Кстати, имейте в виду, что opcache.jit необязателен. Если это свойство опущено, JIT будет использовать значение по умолчанию.
Какое именно значение по умолчанию, спросите вы? Это opcache.jit=tracing .
Постойте, но это же не та странная структура, похожая на битовую маску, которую мы видели ранее? Всё верно: после принятия первоначального RFC авторы признали, что опции, похожие на битовую маску, не слишком удобны для пользователей, поэтому добавили два псевдонима, которые под капотом преобразуются в битовую маску. Это opcache.jit=tracing и opcache.jit=function .
Разница между ними в том, что function JIT пытается оптимизировать код только в рамках одной функции, в то время как tracing JIT может анализировать весь стек-трейс для поиска и оптимизации «горячего» кода. Разработчики ядра рекомендуют использовать tracing JIT, так как он практически всегда даёт наилучшие результаты. Вы можете прочитать об этих результатах в бенчмарках, которые я проводил.
Таким образом, единственный параметр, который вам действительно нужно задать для включения JIT с оптимальной конфигурацией, — это opcache.jit_buffer_size , но если вы хотите указать настройки явно, то добавить opcache.jit также будет неплохой идеей:
opcache.enable=1 opcache.jit_buffer_size=100M opcache.jit=tracing
Комментарии (0)
Пока нет комментариев — будьте первым.