June 2018


blog.udartsev.ru

Компилятор JIT для PHP (Just In Time - компиляция во время выполнения)

Автоорский перевод оригинального поста: https://blog.intracto.com/a-jit-compiler-for-php

Как работает PHP?

PHP - это «язык сценариев» ("scripting language"), это означает, что он напрямую не компилируется в машинный язык.

Когда вы запускаете PHP-программу, Zend Engine анализирует код в абстрактном синтаксическом дереве (AST), а далее переводит его в операционные коды (opcodes). Коды операций - это исполнительные блоки для виртуальной машины Zend (Zend VM). Коды операций довольно низкоуровневые и их гораздо быстрее перевести в машинный код, чем исходный код PHP. PHP имеет расширение OPcache в ядре, чтобы кэшировать эти коды операций.

Если в пару строк, то алгоритм работы PHP следующий: Первый запуск PHP-программы начинается с анализа PHP-кода, далее он кешируется в до кодов операций. При последующем использованиии будут использоваться ранее кэшированные коды операций (OPcache). Поэтому всегда проверяйте загружен ли у вас Zend OPcache и активен ли он.

php

Можно ли сделать исполнение кода быстрее?

1 сентября 2016 года Дмитрий Стогов (один из разработчиков движка PHP) опубликовал сообщение, в котором он анонсировал работу по созданию нового JIT-компилятора для PHP версии 8.

Компилятор «just in time» (JIT) будет принимать выходные данные кодов операций (opcodes) и вместо их интерпретации он скомпилирует их в машинный код и вместо этого вызовет этот код объекта. JIT должен преодолевать неэффективность интерпретации кодов операций при каждом запуске программы. Интересно?

Обратите внимание, что JVM (Java), CLR (.net) и HHVM (PHP для Facebook) используют подход JIT.

Код доступен в ветке jit-dynasm ZendTech https://github.com/zendtech / php-src / tree / jit-dynasm / ext / opcache / jit , поэтому пока не доступен в официальном репозитории PHP.

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

Подходы различаются выбором объекта компиляции (какую часть кода компилировать), как часто и с какой точностью (авт.). Некоторые компиляционные коды JIT используются только один раз, в результате чего компилируется весь объект за раз. Другие могут выбирать один метод (часть кода) и использовать его время от времени и т. д.

Сейчас в PHP все переменные имеют тип данных, объявленные во время компиляции (AOT) - это накладыает ограничения на изменение данных и значений во время выполнения. Но JIT-компиляция работает во время выполнения и может быть быстрее в таких вещах, как переопределение типа даннных, поскольку он может выполнять внутренний анализ процедур во время исполнения. Другими словами, он знает больше о переменных во время выполнения. С другой стороны, первое выполнение JIT, вероятно, медленнее интерпретатора из-за дополнительных шагов перевода, которые он должен обрабатывать.

Я не эксперт в этой области, и все это кажется очень сложным, но очень интересно наблюдать за изменениями в мире PHP. Мне очень любопытно, что из этого получится. Внедрение подобного JIT метода компиляции может сделать PHP более жизнеспособным для интенсивного использования ЦП, такого как машинное обучение, нейросети и т. д.

Безопасность JIT

JIT будет компилировать коды операций для машинного кода и выполнять их. Всё это делается в памяти. Проблемы в том, что по соображениям безопасности память должна быть либо записываемой, либо исполняемой (W ^ X). Но никогда не оба одновременно.

Текущая реализация PHP запрещает запись в буфер JIT во время выполнения, используя системные вызовы mprotect (). Это означает, что PHP-JIT будет компилировать код и записывать его в память и защищать его, чтобы он не изменялся во время выполнения, предотвращая возможные эксплойты.

В настоящее время существует 2 расширения ядра PHP, которые нарушают принцип W ^ X. Phar и PCRE JIT. Но новый PHP JIT в opcache с самого начала учитывает W ^ X, что хорошо.

Я протестировал текущую сборку PHP-JIT в OpenBSD 6.0, в которой по умолчанию включен W ^ X - все работает нормально. Нарушений нет. Обратите внимание, что SELinux также обеспечивает такие виды защиты.

Скорость

Никаких реальных улучшений производительности нет, по крайней мере, для типичных рабочих нагрузок в Интернете. Можно протестировать PHP-JIT тестовым файлом (доступно в репозиториях PHP) следующим образом:

php -d opcache.jit_buffer_size=32M Zend/bench.php

Edit 25 / jun / 2018: Zeef Suraski пишет, что внедрение JIT показывает значительные улучшения производительности большин нагрузках на ЦП (https://externals.io/message/102415).