Понимание Jit В Php 8

Перевод статьи был подготовлен до начала курса.

«Бэкенд-разработчик на PHP»

Понимание JIT в PHP 8



ТЛ;ДР

Компилятор Just In Time в PHP 8 реализован как часть Расширения Opcache и предназначен для компиляции рабочего кода в инструкции процессора во время выполнения.

Это означает, что При использовании JIT некоторые коды операций не требуют интерпретации виртуальной машиной Zend, такие инструкции будут выполняться непосредственно как инструкции уровня процессора.



JIT в PHP 8

Одной из наиболее комментируемых особенностей PHP 8 является компилятор Just In Time (JIT).

Это слышно во многих блогах и сообществах — вокруг этого много шума, но подробностей о работе JIT я пока не нашел.

После неоднократных попыток и разочарований в поиске полезной информации я решил изучить исходный код PHP. Объединив мои небольшие знания языка C и всю разрозненную информацию, которую мне удалось собрать на данный момент, мне удалось подготовить эту статью и надеюсь, что она поможет вам лучше понять JIT PHP. Чтобы упростить ситуацию: когда JIT работает должным образом, ваш код не будет выполняться через виртуальную машину Zend, а будет выполняться непосредственно как набор инструкций на уровне процессора.

В этом вся идея.

Но чтобы лучше это понять, нам нужно подумать о том, как работает PHP внутри.

Это не очень сложно, но требует некоторого представления.

Я уже написал статью с кратким описанием обзор того, как работает PHP .

Если эта статья покажется вам слишком сложной, просто прочтите предыдущую и вернитесь к ней.

Это должно немного облегчить задачу.



Как выполняется PHP-код?

Все мы знаем, что PHP — интерпретируемый язык.

Но что это на самом деле означает? Всякий раз, когда вы хотите выполнить код PHP, будь то фрагмент или целое веб-приложение, вам придется использовать интерпретатор PHP. Наиболее часто используемые из них — PHP FPM и интерпретатор CLI. Их работа очень проста: получить php-код, интерпретировать его и вернуть результат. Это общая картина для каждого интерпретируемого языка.

Некоторые шаги могут отличаться, но общая идея одна и та же.

В PHP это работает так: Код PHP считывается и преобразуется в набор ключевых слов, известных как токены.

Этот процесс позволяет интерпретатору понять, в какой части программы написан тот или иной фрагмент кода.

Этот первый шаг называется лексингом или токенизацией.

.

Имея на руках токены, интерпретатор PHP проанализирует эту коллекцию токенов и попытается найти в них смысл.

В результате абстрактное синтаксическое дерево (AST) создается с помощью процесса, называемого анализ .

AST — это набор узлов, которые указывают, какие операции следует выполнить.

Например, «эхо 1 + 1» на самом деле должно означать «распечатать результат 1 + 1» или, что более реалистично, «распечатать операцию, операция равна 1 + 1».

Например, наличие AST значительно облегчает понимание операций и их приоритета.

Преобразование этого дерева во что-то, что можно выполнить, требует промежуточного представления IR, которое в PHP мы называем кодом операции.

Процесс преобразования AST в рабочий код называется компиляцией.

Теперь, когда у нас есть коды операций, происходит самое интересное: производительность код! В PHP есть движок Zend VM, который способен получать список кодов операций и выполнять их.

После выполнения всех кодов операций программа завершается.

Чтобы было немного понятнее, я составил диаграмму:

Понимание JIT в PHP 8

Упрощенная схема процесса интерпретации PHP. Как видите, довольно просто.

Но здесь есть узкое место: какой смысл лексировать и анализировать код каждый раз, когда вы его выполняете, если ваш PHP-код может даже не меняться так часто? В конце концов, нас интересуют только коды операций, верно? Верно! Вот почему он существует Расширение Opcache .



Расширение Opcache

Расширение Opcache поставляется с PHP, и обычно нет особых причин его отключать.

Если вы используете PHP, вам, вероятно, следует включить Opcache. Что он делает, так это добавляет слой общего онлайн-кэша для кодов операций.

Его задача — получить коды операций, недавно сгенерированные из нашего AST, и кэшировать их, чтобы будущие исполнения могли легко пропускать фазы лексического анализа и синтаксического анализа.

Вот схема того же процесса с учетом расширения Opcache:

Понимание JIT в PHP 8

Процесс интерпретации PHP с помощью Opcache. Если файл уже был проанализирован, php извлекает для него кэшированный код операции, а не анализирует его снова.

Просто поразительно, как красиво пропускаются этапы лексического анализа, синтаксического анализа и компиляции.

Примечание : Здесь он проявляет себя лучше всего Функция предварительной загрузки PHP 7.4 ! Это позволяет вам указать PHP FPM проанализировать вашу кодовую базу, преобразовать ее в коды операций и кэшировать их еще до того, как вы что-либо выполните.

Вы можете начать задаваться вопросом, причем тут JIT, верно?! По крайней мере, я на это надеюсь, поэтому и пишу эту статью.



Что делает компилятор Just In Time?

Выслушав объяснение Зива выпуск подкастов PHP и JIT от PHP Internals News Мне удалось получить некоторое представление о том, что на самом деле должен делать JIT. Если Opcache позволяет быстрее получать код операции и отправлять его непосредственно на виртуальную машину Zend, то JIT предназначен для того, чтобы он работал вообще без виртуальной машины Zend. Zend VM — это программа, написанная на языке C, которая действует как прослойка между операционным кодом и самим процессором.

JIT генерирует скомпилированный код во время выполнения, поэтому php может пропустить виртуальную машину Zend и перейти непосредственно к процессору.

.

Теоретически мы должны получить от этого производительность.

Поначалу это звучало странно, потому что для компиляции машинного кода нужно написать очень специфическую реализацию для каждого типа архитектуры.

Но на самом деле это вполне возможно.

JIT-реализация в PHP использует библиотеку DynASM (динамический ассемблер) , который отображает набор инструкций ЦП в определенном формате в ассемблерный код для множества различных типов ЦП.

Таким образом, компилятор Just In Time преобразует операционный код в машинный код, зависящий от архитектуры, с помощью DynASM. Хотя одна мысль все еще не давала мне покоя.

Если фронтальная загрузка может разобрать код PHP в рабочий код перед выполнением, а DynASM может скомпилировать рабочий код в машинный код (компиляция «точно в срок»), почему, черт возьми, мы не скомпилируем PHP на месте, используя компиляцию с опережением времени?! Одна из вещей, о которой заставил меня задуматься этот эпизод подкаста, заключалась в том, что PHP слабо типизирован, а это означает, что часто PHP не знает, какого типа переменная, пока виртуальная машина Zend не попытается выполнить определенный код операции.

Это можно понять, посмотрев тип объединения zend_value , который имеет множество указателей на различные представления типов переменной.

Всякий раз, когда виртуальная машина Zend пытается извлечь значение из zend_value, она использует такие макросы, как ЗSTR_VAL , которые пытаются получить доступ к указателю строки из объединения значений.

Например, этот обработчик виртуальной машины Zend должен обрабатывать «меньше или равно» (<=) expression. See how it branches into many different code paths to guess the operand types. Дублирование такой логики вывода типа с использованием собственного кода невозможно и потенциально может еще больше замедлить работу.

Окончательная компиляция после оценки типов также не является хорошим вариантом, поскольку компиляция в машинный код — это задача, нагружающая процессор.

Поэтому компилировать ВСЕ во время выполнения — плохая идея.



Как ведет себя компилятор Just In Time?

Теперь мы знаем, что не можем выводить типы для создания достаточно хорошей прямой компиляции.

Мы также знаем, что компиляция во время выполнения стоит дорого.

Чем JIT может быть полезен для PHP? Чтобы сбалансировать это уравнение, JIT PHP пытается скомпилировать только те несколько кодов операций, которые, по его мнению, того стоят. Для этого он профилирует коды операций, выполняемые виртуальной машиной Zend, и проверяет, какие из них имеет смысл компилировать.

(в зависимости от вашей конфигурации) .

Когда определенный код операции компилируется, он делегирует выполнение этому скомпилированному коду, а не виртуальной машине Zend. Это выглядит так, как показано на схеме ниже:

Понимание JIT в PHP 8

Процесс интерпретации 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

PHP: статические анализаторы кода
Теги: #программирование #php #php8 #jit

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.