# iOS and «JIT»
JIT stands for just-in-time compilation, and (although not specific to Java), it serves as a method of executing Java code quicker then the alternatives, by compiling as-needed functions at runtime, rather than its slower alternatives to compile all code at runtime repeatedly or writing in a less portable, hardware-level format. It is the fastest method to running Minecraft: Java Edition on mobile devices, essentially required for a usable experience.
On Apple’s mobile platforms (iOS, iPadOS, tvOS, in our case), a security feature known as code-signing is deeply rooted in the operation of the system. It serves to verify that applications haven’t been tampered with after they were installed by a user, so that a malicious payload cannot be installed after-the-fact by a malicious someone. However, due to the process in which code-signing works, this prevents JIT from working properly as it works by reading code, modifying it, and then executing it in some fashion (whether that be by copying the code to a new location in memory, executing directly, or mirroring).
# What does this mean for PojavLauncher?
# Jailbroken devices
Jailbroken device users don’t need to worry about JIT requirements, as PojavLauncher is built to detect jailbreaks and automatically enable new features.
# Unjailbroken devices
Unjailbroken users can see two different outcomes, based on what they used to sideload PojavLauncher.
# TrollStore
If you used TrollStore to sideload PojavLauncher, good news: PojavLauncher takes advantage of the extended capabilities with TrollStore and automatically enables JIT when launched.
# Normal sideload
If you sideload normally, you will need to enable JIT in some way. The most common method is to attach a debug server to the application while it’s running — JITStreamer, AltStore, and Jitterbug all use this with their Jit enabling functions. The only downside to this method is that you are required to be connected to a WiFi network in order to enable JIT.
PojavLauncher has native integration with JITStreamer, and we recommend following the steps available here
(opens new window) to get started. Alternatively, you can use AltStore
(opens new window) . Methods not listed here are not confirmed or recommended by us for use with PojavLauncher.
PHP 8: Как включить JIT

JIT-компиляторы очень сложны, потому что для того, чтобы добиться от них хорошей производительности, обычно нужно выбирать, какие именно части промежуточного языка должны компилироваться в машинный код, а какие нет. Преобразование в машинный код не всегда происходит быстрее, в зависимости от конкретных деталей кода и рассматриваемого языка. Кроме того, процесс преобразования упрощенного кода в машинный код может занять больше времени, чем просто однократный запуск упрощенного кода и мгновенного завершение его работы.
По этой причине большинство JIT-компиляторов анализируют код во время его выполнения, чтобы определить, какие части дают наилучшую отдачу, а затем компилируют только эти биты. Т еоретически, о тличный результат состоит в том, что программа буквально становится быстрее по мере ее выполнения, а JIT-компилятор в виртуальной машине узнает, какие части кода нужно оптимизировать.
Java была первым широко распространенным языком, включившим JIT в свою виртуальную машину. Большинство основных движков Javascript теперь тоже. И, начиная с PHP 8.0, к этому списку присоединился и PHP.
PHP JIT
JIT в PHP появилась давно. На самом деле он находился в разработке в течение нескольких лет и почти был выпущен в PHP 7.4. Работа по созданию JIT в PHP была тем импульсом, который привел к серьезному переписыванию движка, что дало версии 7.0 значительный прирост производительности.
PHP JIT построен как расширение кеша опкодов. Это означает, что его можно включать и отключать либо при сборке самого PHP, либо во время выполнения через php.ini.
Честно говоря, настройка JIT — один из самых запутанных способов настройки расширения PHP, который я когда-либо видел. К счастью, есть несколько сокращений конфигурации, которые упрощают настройку. Тем не менее, будет очень полезным знать как настраивать JIT, так что приступим.
Кратко для системных администраторов
В PHP 8.0 JIT как бы включен по умолчанию, но и выключен. Требуется только минимальная конфигурация php.ini, подобная этой:
opcache.enable=1
opcache.enable_cli=1
opcache.jit_buffer_size=128M
Как настроить JIT в PHP 8
Прежде всего, JIT будет работать, только если включен opcache, хоть по умолчанию в PHP он и включен, вы все равно должны убедиться, что в вашем php.ini файле установлено значение opcache.enable = 1. Включение самого JIT осуществляется путем установки в php.ini параметра opcache.jit_buffer_size в ненулевое значение. Это контролирует, сколько места в памяти JIT может заполнить оптимизированным машинным кодом. Однако больше не всегда лучше, поскольку JIT также может тратить время на компиляцию кода, который на самом деле не выигрывает от компиляции.
Обратите внимание, что если вы запускаете PHP через командную строку, вы также можете передать эти параметры через флаг -d вместо того, чтобы добавлять их в php.ini:
php -d opcache.enable=1 -d opcache.jit_buffer_size=128M
Если эта директива отсутствует, то значение по умолчанию будет равно 0, и JIT не будет работать.
Так же, если вы тестируете JIT в сценарии CLI, вы должны использовать opcache.enable_cli для включения opcache:
php -d opcache.enable_cli=1 -d opcache.jit_buffer_size=128M
Разница между 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 (вызов стандартных обработчиков ВМ) |
| 2 | выборочное встраивание обработчика ВМ |
| 3 | оптимизированный JIT на основе статического вывода типов отдельной функции |
| 4 | оптимизированный JIT на основе статического вывода типов и дерева вызовов |
| 5 | оптимизированная JIT на основе статического вывода типов и анализа внутренних процедур |
T — триггер JIT
| 0 | JIT все функции при первой загрузке скрипта |
| 1 | Функция JIT при первом выполнении |
| 2 | Профилировать по первому запросу и компилировать горячие функции по второму запросу |
| 3 | Профилировать на лету и компилировать горячие функции |
| 4 | |
| 5 | Трассировка JIT |
R — распределение регистров
| 0 | не выполнять распределение регистров |
| 1 | использовать локальный распределитель регистров liner-scan |
| 2 | использовать глобальный распределитель регистров liner-scan |
C — флаги оптимизации CPU
| 0 | нет |
| 1 | включить генерацию инструкций AVX |
Одно небольшое уточнение: Перечислять эти параметры нужно в обратном порядке, поэтому первая цифра представляет C значение, вторая — R, третья — T и четвертая — O. Почему просто не были добавлены четыре записи конфигурации, я не понимаю, вероятно, чтобы ускорить настройку JIT, может быть.
В любом случае, рекомендуется устанавливать значение 1255 в качестве наиболее оптимального, тогда будет выполняться максимальный «джитинг», используя JIT трассировки, глобальный распределитель регистров liner-scan — что бы это ни было, черт возьми, и позволяет генерировать инструкции AVX.
Итак, ваши ini-настройки (или флаги -d ) должны иметь следующие значения:
opcache.enable=1
opcache.jit_buffer_size=128M
opcache.jit=1255
Имейте в виду, что необязательно настраивать opcache.jit. JIT будет использовать значение по умолчанию, если это свойство не указано. А какой по умолчанию, спросите вы? А вот какое opcache.jit=tracing.
Что еще за трассировка, спросите вы, это совсем не та странная структура, похожая на битовую маску, которую мы видели ранее. И вы будете правы: после того, как был принят исходный RFC, контрибьюторы ядра PHP осознали, что параметры-шифры, подобные битовой маске, не очень удобны для пользователя, поэтому они добавили два человечески понятные псевдонима, которые транслируются в ту самую битовую маску под капотом. Есть opcache.jit=tracing и opcache.jit=function.
Разница между ними заключается в том, что при параметре функции JIT пытается оптимизировать код только в рамках одной функции, в то время как JIT трассировка может просматривать всю трассировку стека, чтобы идентифицировать и оптимизировать горячий код. PHP Internals рекомендует использовать JIT-трассировку, потому что она почти всегда дает наилучшие результаты.
Таким образом, единственно что от вас потребуется, чтобы включить JIT с его оптимальной конфигурацией — это установка opcache.jit_buffer_size, но если вы хотите установить конфигурацию явно, включение параметра opcache.jit будет не такой уж плохой идеей:
opcache.enable=1
opcache.jit_buffer_size=128M
opcache.jit=tracing
Отладка JIT (opcache.jit_debug)
PHP JIT обеспечивает способ выдачи отладочной информации JIT путем установки конфигурации INI. Когда установлен определенный флаг, он выводит код сборки для дальнейшей проверки.
opcache.jit_debug=1
Директива opcache.jit_debug принимает значение битовой маски для включения определенных функций. В настоящее время он принимает значение в диапазоне от 1 (0b1) до 20 двоичных разрядов. Значение 1048576 представляет максимальный уровень вывода отладки.
php -d opcache.jit_debug=1 test.php
TRACE-1$/test.php$7: ; (unknown)
sub $0x10, %rsp
mov $EG(jit_trace_num), %rax
mov $0x1, (%rax)
.L1:
cmp $0x4, 0x58(%r14)
jnz jit$$trace_exit_0
cmp $0x4e20, 0x50(%r14)
.
JIT поддерживает несколько других параметров конфигурации, чтобы настроить, сколько вызовов функций делает ее «горячей» функцией, которая затем компилируется JIT, и порог для определения того, какие функции следует «JIT’ировать», на основе процента общих вызовов функций.
Улучшит ли JIT производительность?
Короче, «это зависит от обстоятельств». Для веб-приложений может и не очень, но вот для PHP как экосистемы даже может чрезмерно.
PHP, по замыслу, обычно работает в конфигурации без совместного использования ресурсов. После обработки каждого запроса приложение полностью закрывается. Это дает JIT очень мало времени на анализ и оптимизацию кода, тем более, что большая часть кода в типичном веб-запросе выполняется только один раз, поскольку запрос обрабатывается линейно. Кроме того, большей частью этих приложений часто является ввод-вывод (в основном, общение с базой данных), и JIT вообще не может с этим помочь. Результаты тестов, которые были опубликованы на данный момент, показывают, что JIT предлагает лишь незначительное повышение производительности в типичных приложениях PHP, запускаемых через PHP-FPM.
JIT потенциально может быть действительно полезен в тех случаях, где PHP сегодня совсем не рассматривается. Реальные преимущества будут видны в работе постоянных демонов, парсерах, машинном обучении и других длительных процессах, интенсивно использующих CPU.
PHP-Parser — это «анализатор PHP, написанный на PHP». Он от того же Никиты Попова, и используется многими инструментами статического анализа, представленными сегодня на рынке, такими как, например, PHPStan и пр. Никита сообщил, что PHP-Parser в некоторых случаях работает в два раза быстрее с новым движком JIT.
Асинхронные приложения, написанные на React PHP или AmPHP , вероятно, также увидят заметное улучшение, хотя и не настолько, как правило, они выполняют много операций ввода-вывода (где JIT бесполезен).
Может быть для вас будет шоком узнать, что для PHP доступны библиотеки машинного обучения, такие как Rubix ML или PHP-ML. Они не так широко используются, как их собраты на Python, отчасти потому, что при интерпретации они, как правило, медленнее, чем библиотеки C с красивыми оболочками Python. Однако с JIT эти задачи с интенсивным использованием CPU могут оказаться такими же быстрыми или, возможно, даже более быстрыми, чем те, которые доступны на других языках.
PHP больше не является «просто самым быстрым из основных языков веб-сценариев». Теперь это жизнеспособный высокопроизводительный язык общей обработки данных, с передачей постоянных воркеров, машинного обучения и других высокопроизводительных задач, в руки миллионов PHP-разработчиков по всему миру.
В первую очередь мы должны поблагодарить Дмитрия Стогова и Зеева Сураски за многолетние усилия по реализации этого RFC .
Jit компилятор java
Правильно ли я понимаю, что jit начинает работу с вызова интерпретатора java. При этом происходят 2 вещи: jit -код не оптимизируется, не ждет интерпретатора.
Отслеживать
задан 4 дек 2013 в 18:50
2,434 3 3 золотых знака 26 26 серебряных знаков 42 42 бронзовых знака
я не знаю, что такое «jit -код»
4 дек 2013 в 20:00
jit-компилятор может посмотреть на сгенерированный машинный код и сделать новую версию.
4 дек 2013 в 21:29
а где бы почитать про алгоитмы работы JIT— компилятора, интерпретатора. PS jit-код в моем понимании — это байт-код , прошу прощения
5 дек 2013 в 8:28
> PS jit-код в моем понимании — это байт-код , прошу прощения тогда все стает ещё непонятнее. > а где бы почитать про алгоитмы работы JIT- компилятора начните с чтения документации — docs.oracle.com/cd/E13150_01/jrockit_jvm/jrockit/geninfo/…
5 дек 2013 в 8:41
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
@voipp, что Вы имеете в виду, когда пишете jit-код не ждет интерпретатора?
(я подразумеваю, что jit-кодом Вы называете JVM byte code)
А вообще, скорее всего принципиально Вы правильно понимаете суть дела.
Сначала байт-код интерпретируется и при этом накапливается некоторая статистика. После нескольких интерпретаций одного и того же фрагмента байт-кода он заменяется вызовом создаваемых «на лету» машинных команд.
Просто наберите в гугле
jit компиляция
Понимаем JIT в PHP 8
Одной из наиболее комментируемых фич PHP 8 является компилятор Just In Time (JIT). Он на слуху во множестве блогов и сообществ — вокруг него много шума, но пока я нашел не очень много подробностей о работе JIT в деталях.
После многократных попыток и разочарований найти полезную информацию, я решил изучить исходный код PHP. Совмещая свои небольшие познания языка С и всю разбросанную информацию, которую я смог собрать до сих пор, я сумел подготовить эту статью и надеюсь, что она поможет вам лучше понять JIT PHP.
Упрощая вещи: когда JIT работает должным образом, ваш код не будет выполняться через Zend VM, вместо этого он будет выполняться непосредственно как набор инструкций уровня процессора.
В этом вся идея.
Но чтобы лучше это понять, нам нужно подумать о том, как php работает внутри. Это не очень сложно, но требует некоторого введения.
Я уже писал статью с кратким обзором того, как работает php. Если вам покажется, что эта статья становится чересчур сложной, просто прочитайте ее предшественницу и возвращайтесь. Это должно немного облегчить ситуацию.
Как выполняется PHP-код?
Мы все знаем, что php — интерпретируемый язык. Но что это на самом деле означает?
Всякий раз, когда вы хотите выполнить код PHP, будь то фрагмент или целое веб-приложение, вам придется пройти через интерпретатор php. Наиболее часто используемые из них — PHP FPM и интерпретатор CLI. Их работа очень проста: получить код php, интерпретировать его и выдать обратно результат.
Это обычная картина для каждого интерпретируемого языка. Некоторые шаги могут варьироваться, но общая идея та же самая. В PHP это происходит так:
- Код PHP читается и преобразуется в набор ключевых слов, известных как токены (Tokens). Этот процесс позволяет интерпретатору понять, в какой части программы написан каждый фрагмент кода. Этот первый шаг называется лексирование (Lexing) или токенизация (Tokenizing).
- Имея на руках токены, интерпретатор PHP проанализирует эту коллекцию токенов и постарается найти в них смысл. В результате абстрактное синтаксическое дерево (Abstract Syntax Tree — AST) генерируется с помощью процесса, называемого синтаксическим анализом (parsing). AST представляет собой набор узлов, указывающих, какие операции должны быть выполнены. Например, «echo 1 + 1» должно фактически означать «вывести результат 1 + 1» или, более реалистично, «вывести операцию, операция — 1 + 1».
- Имея AST, например, гораздо проще понять операции и их приоритет. Преобразование этого дерева во что-то, что может быть выполнено, требует промежуточного представления (Intermediate Representation IR), которое в PHP мы называем операционный код (Opcode). Процесс преобразования AST в операционный код называется компиляцией.
- Теперь, когда у нас есть опкоды, происходит самое интересное: выполнение кода! PHP имеет движок под названием Zend VM, который способен получать список опкодов и выполнять их. После выполнения всех опкодов программа завершается.
Чтобы сделать это немного нагляднее, я составил диаграмму:

Упрощенная схема процесса интерпретации PHP.
Достаточно прямолинейно, как вы можете заметить. Но здесь есть и узкое место: какой смысл лексировать и парсить код каждый раз, когда вы его выполняете, если ваш php-код может даже не меняется так часто?
В конце концов, нас интересуют только опкоды, верно? Правильно! Вот зачем существует расширение Opcache.
Расширение Opcache
Расширение Opcache поставляется с PHP, и, как правило, нет особых причин его деактивировать. Если вы используете PHP, вам, вероятно, следует включить Opcache.
Что он делает, так это добавляет слой оперативного общего кэша для опкодов. Его задача состоит в том, чтобы извлекать опкоды, недавно сгенерированные из нашего AST, и кэшировать их, чтобы при дальнейших выполнениях можно было легко пропустить фазы лексирования и синтаксического анализа.
Вот схема того же процесса с учетом расширения Opcache:

Поток интерпретации PHP с Opcache. Если файл уже был проанализирован, php извлекает для него кэшированный операционный код, а не анализирует его заново.
Это просто завораживает, как красиво пропускаются шаги лексирования, синтаксического анализа и компиляции.
Примечание: именно здесь лучше всего себя проявляет функция предварительной загрузки PHP 7.4! Это позволяет вам сказать PHP FPM анализировать вашу кодовую базу, преобразовывать ее в опкоды и кэшировать их даже до того, как вы что-либо выполните.
Вы можете начать задумываться, а куда сюда можно прилепить JIT, верно?! По крайней мере я на это надеюсь, именно поэтому я и пишу эту статью…
Что делает компилятор Just In Time?
Прослушав объяснение Зива в эпизоде подкастов PHP и JIT от PHP Internals News, мне удалось получить некоторое представление о том, что на самом деле должен делать JIT…
Если Opcache позволяет быстрее получать операционный код, чтобы он мог переходить непосредственно к Zend VM, JIT предназначить заставить его работать вообще без Zend VM.
Zend VM — это программа, написанная на C, которая действует как слой между операционным кодом и самим процессором. JIT генерирует скомпилированный код во время выполнения, поэтому php может пропустить Zend VM и перейти непосредственно к процессору. Теоретически мы должны выиграть в производительности от этого.
Поначалу это звучало странно, потому что для компиляции машинного кода вам нужно написать очень специфическую реализацию для каждого типа архитектуры. Но на самом деле это вполне реально.
Реализация JIT в PHP использует библиотеку DynASM (Dynamic Assembler), которая отображает набор команд ЦП в конкретном формате в код сборки для многих различных типов ЦП. Таким образом, компилятор Just In Time преобразует операционный код в машинный код для конкретной архитектуры, используя DynASM.
Хотя одна мысль все-таки не давала мне покоя…
Если предварительная загрузка способна парсить php-код в операционный перед выполнением, а DynASM может компилировать операционный код в машинный (компиляция Just In Time), почему мы, черт возьми, не компилируем PHP сразу же на месте, используя Ahead of Time компиляцию?!
Одна из мыслей, на которые меня натолкнул эпизода подкаста, заключалась в том, что PHP слабо типизирован, то есть часто PHP не знает, какой тип имеет переменная, пока Zend VM не попытается выполнить определенный опкод.
Это можно понять, посмотрев на тип объединения zend_value, который имеет много указателей на различные представления типов для переменной. Всякий раз, когда виртуальная машина Zend пытается извлечь значение из zend_value, она использует макросы, подобные ZSTR_VAL, которые пытаются получить доступ к указателю строки из объединения значений.
Дублирование такой логики вывода типов с помощью машинного кода неосуществимо и потенциально может сделать работу еще медленнее.
Финальная компиляция после того, как типы были оценены, также не является хорошим вариантом, потому что компиляция в машинный код является трудоемкой задачей ЦП. Так что компиляция ВСЕГО во время выполнения — плохая идея.
Как ведет себя компилятор Just In Time?
Теперь мы знаем, что не можем вывести типы, чтобы генерировать достаточно хорошую опережающую компиляцию. Мы также знаем, что компиляция во время выполнения стоит дорого. Чем же может быть полезен JIT для PHP?
Чтобы сбалансировать это уравнение, JIT PHP пытается скомпилировать только несколько опкодов, которые, по его мнению, того стоят. Для этого он профилирует коды операций, выполняемые виртуальной машиной Zend, и проверяет, какие из них имеет смысл компилировать. (в зависимости от вашей конфигурации).
Когда определенный опкод компилируется, он затем делегирует выполнение этому скомпилированному коду вместо делегирования на Zend VM. Это выглядит как на диаграмме ниже:

Поток интерпретации PHP с JIT. Если они уже скомпилированы, опкоды не выполняются через Zend VM.
Таким образом, в расширении Opcache есть пара инструкций, определяющих, должен ли определенный операционный код быть скомпилирован или нет. Если да, то компилятор преобразует его в машинный код с помощью DynASM и выполняет этот новый сгенерированный машинный код.
Интересно, что, поскольку в текущей реализации есть ограничение в мегабайтах для скомпилированного кода (также настраиваемое), выполнение кода должно иметь возможность беспрепятственного переключения между JIT и интерпретируемым кодом.
Кстати, эта беседа Бенуа Жакемона о JIT от php ОЧЕНЬ помогла мне разобраться во всем этом.
Я до сих пор не уверен в том, в каких конкретных случаях происходит компиляция, но я думаю, что пока не очень хочу это знать.
Так что, вероятно, ваш прирост производительности не будет колоссальным
Я надеюсь, что сейчас гораздо понятнее, ПОЧЕМУ все говорят, что большинство приложений php не получат больших преимуществ в производительности от использования компилятора Just In Time. И почему рекомендация Зива для профилирования и эксперимента с различными конфигурациями JIT для вашего приложения — лучший путь.
Скомпилированные опкоды обычно будут распределены между несколькими запросами, если вы используете PHP FPM, но это все равно не изменит правила игры.
Это потому, что JIT оптимизирует операции с процессором, а в настоящее время большинство php-приложений в большей степени завязаны на вводе/выводе, нежели на чем-либо еще. Не имеет значения, скомпилированы ли операции обработки, если вам все равно придется обращаться к диску или сети. Тайминги будут очень похожи.
Если только.
Вы делаете что-то не завязанное на ввод/вывод, например, обработку изображений или машинное обучение. Все, что не касается ввода/вывода, получит пользу от компилятора Just In Time. Это также причина, по которой люди сейчас говорят, что они склоняются больше к написанию нативных функций PHP, написанных на PHP, а не на C. Накладные расходы не будут разительно отличаться, если такие функции будут скомпилированы в любом случае.
Интересное время быть программистом PHP…
Я надеюсь, что эта статья была полезна для вас, и вам удалось лучше разобраться, что такое JIT в PHP 8. Не стесняйтесь связываться со мной в твиттере, если вы хотите добавить что-то, что я мог забыть здесь, и не забудьте поделиться этим со своими коллегами-разработчиками, это, несомненно, добавит немного пользы вашим беседам! — @nawarian