JPHP

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 02:42, 22 мая 2016.
JPHP
Спроектировано Дмитрий Зайцев
Первый   появившийся 2014
Лицензия Apache License 2.0
Портал: http://j-php.net/

JPHP- Это альтернативная реализация PHP для JavaVM с поддержкой JIT. Я начал проект в одиночку в октябре 2013 года и за 4 месяца реализовал компилятор php в байткод JVM. Язык поддерживается на уровне PHP 5.3, частично поддерживаются возможности PHP 5.4 и 5.5. По своей идеологии проект напоминает JRuby и Jython.

Цель проекта

  • Использование java библиотек в PHP
  • Увеличить производительность за счет JIT + JVM
  • Заменить несогласованный и уродливый рантайм Zend PHP на что-то более приличное
  • Позволить писать на PHP не только под Web
  • Реализовать поддержку юникода и многопоточности

Технические детали

Весь язык написан с нуля на Java с применением библиотеки ASM для генерации байткода, её используют все популярные jvm языки, например Groovy. В качестве системы сборки был выбран Gradle.

Cтек технологий Java предоставляет очень удобные условия для написания jvm языка. Не нужно писать свою VM с JIT, сборщик мусора и система классов уже реализованы, не болит голова о кроссплатформенности, а сам байткод jvm очень легок в понимании. Однако было много и подводных камней, больше всего из-за магии самого языка php.

JIT позволил увеличить производительность в 1-10 раз, в зависимости от тестов, в среднем в 1.5-3 раза на реальном коде.

Совместимость с PHP?

Стоит различать язык и библиотеки к нему, поэтому совместимость на уровне языков и библиотек это разные вещи. Уже в начале разработки я понял, что написать с должной совместимостью все библиотеки PHP невозможно одному человеку. Я решил сконцентрироваться лишь на языке, хотя и реализовал базовые вещи, такие как spl autoloading, Reflection, ob_* функции, <? ... ?> и многое другое.

JPHP проходит около 300+ юнит-тестов от оригинального Zend PHP, среди которых тестирование ООП, функций, операторов и т.п. Есть также и свои тесты. Это помогает быть уверенным, что язык работает должным образом.

  • Язык на уровне PHP 5.3 (за исключением goto)
  • Typehinting для callable (5.4)
  • Короткий синтаксис для массивов (5.4)
  • Class::{expr}(), (new Foo)->bar() (5.4)
  • try… finally (5.5)
  • Array and string literal dereferencing (5.5)
  • Function array dereferencing foo()[0] (5.4)
  • Системная константа class для получения имени класса (5.5)

JIT и производительность

Как вы думаете, какой код может влиять на производительность? Если вы хорошо знакомы с Facebook HHVM, то думаю вы знаете какой. Основная проблема производительности PHP это глобальное пространство для переменных, магия переменных, просто магия, когда можно обращаться к переменной по имени во время выполнения. По этой причине JPHP по-разному может компилировать один и тот же код. Там, где нет магии с переменными, компилятор преобразует переменные в индексы и во время выполнения сразу обратится к ним по индексу.

$var = 'foobar';
$x = 'var';
${$x} = 'chanched'; // магия переменных
$global_var = 100500;
include 'file.php'; // магия переменных, в include нужно передать global scope с именами
function foobar() {
   $x = 100500;
   $var = get_defined_vars(); // опять магия переменных
}

Поэтому, когда предполагается обращение к переменным по имени во время выполнения, JPHP сохраняет таблицу имен переменных, а когда нет — не сохраняет и обращается к переменным сразу по индексам.

Неправильно подобранные переменные примерно в 2 раза замедляют ваш код в JPHP. В Zend PHP код одинаково работает при любых условиях.

Оптимизатор

Оптимизатор JPHP умеет довольно много, вот небольшой список его возможностей.

Константные выражения

$x = (20 + 30) . 'foobar';  // посчитается во время компиляции 1 раз

Статические константы

Есть такие константы, о которых JPHP знает на момент компиляции, а есть динамические константы, объявленные через define. Статические это системные константы __FILE__, __DIR__, __CLASS__, константы java расширений, константы, которые объявлены в рамках одного файла через const. Все их можно заменить во время компиляции:

include __DIR__ . '/core.php'; // конкатенация произойдет во время компиляции

Immutable функции и методы

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

for ($i = 0; $i < 100500; $i++){
    $x = cos(1) + 3; // посчитается 1 раз во время компиляции, а не 100500
}

В данном примере функция cos() системная и immutable, а параметр переданный функции, является константой, поэтому результат cos(1) никогда не изменится.

К immutable относятся и функции/методы, объявленные программистом, которые не имеют параметров и возвращают константное выражение. Вызов такой функции в JPHP сравним по скорости с обращением к константе, например:

function getVersion(){
    return '1.0';
}

$x = getVersion(); // быстрый вызов

define('VERSION', '1.0');
$x = VERSION; // стоимость вызова константы = стоимости вызова предыдущей функции

Невыполнимые условия

Если в if или elseif у вас константное выражение, которое всего ложно, компилятор отбросит лишнее условие совсем. Пока поддерживаются лишь if, else и elseif. Например, это бывает очень полезным:

if (TRACE_ENABLED){ // если TRACE_ENABLED = false, то компилятор сможет удалить мертвый код
     log("Log text...");
}

Кэширование классов, функций, байткода?

Модель работы JPHP позволяет хранить скомпилированный код в памяти, т.е. классы и функции. Один раз скомпилированный и загруженный класс будет использован многократно. В перспективе это позволит писать http сервера, фреймворки по производительности не уступающие Phalcon на самом PHP. Данные легко хранить между запросами, а http сервер можно написать на самом php, о чем я расскажу ниже.

В JPHP есть специальный класс Environment, который позволяет создавать изолированные окружения для выполнения скриптов, это чем-то похоже на sandbox из расширения runkit. У каждого такого окружения свой набор классов, функций и глобальных переменных.

  • Environment — изолированное окружение для выполнения скриптов
  • Подобен sandbox из runkit
  • Нужен для гибкой реализации многопоточности
  • Позволяет организовать HOT reload схему работы
  • Окружения могут взаимодействовать друг с другом

Список литературы