...

воскресенье, 23 марта 2014 г.

JPHP — Новый движок php для Java VM + JIT

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

Я подготовил небольшую презентацию, которая расскажет о проекте и не отнимет у вас много времени:







Есть желание рассказать о проекте как можно больше, но боюсь все в одну статью не уместить. Надеюсь получилось не слишком сумбурно.




  • Лицензия: Apache License 2.0

  • Адрес проекта: http://ift.tt/1h7EVJj

  • Совместимость: JavaVM 1.6+ (Linux, Windows, MacOS, работает везде где есть Java).


Цель проекта




JPHP это не замена для Zend PHP или Facebook HHVM, потому что в планах не было реализации всех runtime библиотек zend php, таких как CURL, PRCE, PDO и т.п. Основные причины, побудившие начать проект были следующие:

  • Это был эксперимент

  • Использование java библиотек в PHP

  • Увеличить производительность за счет JIT + JVM

  • Заменить несогласованный и уродливый рантайм Zend PHP на что-то более приличное

  • Позволить писать на PHP не только под Web

  • Реализовать поддержку юникода и многопоточности


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




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

К моему удивлению, стек технологий 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)


На подходе реализация трейтов из php 5.4.


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 умеет довольно много, вот небольшой списочек его возможностей.

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



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


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

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



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


3. 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; // стоимость вызова константы = стоимости вызова предыдущей функции


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

Если в 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 схему работы

  • Окружения могут взаимодействовать друг с другом


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


Как попробовать? Собрать тестовый проект?




Для этого вам нужно установить Java (1.6+) и систему сборки Gradle. Скачать исходники из git репозитория. JetBrains IDEA позволяет импортировать проект из build.gradle. На данном этапе есть тестовый проект в папке jphp-example-project. Этот проект собирается в выполняемый jar файл, внутри которого находятся исходники php. При запуске jar выполняется bootstrap.php файл. Собрать jar можно с помощью команды:

gradle jar




Или сразу запустить через:

gradle exampleStart


Проект собирается в папке build/libs/ в jar файл.


GUI? Программы?




Я также написал расширение-обертку для Swing (библиотека Java для GUI). Она позволит вам создавать кроссплатформенные GUI программы. Для тех кто знаком со Swing, хочу обнадежить — я довольно сильно упростил апи и систему layouts. Небольшое окошко на GUI:

use php\lang\System;
use php\swing\SwingUtilities;
use php\swing\UIForm;
use php\swing\UIDialog;
use php\swing\UIButton;

SwingUtilities::invokeLater(function(){
$form = new UIForm();
$form->size = [500, 500];
$form->moveToCenter();
$form->visible = true;

$p = new UIButton();
$p->size = [300, 40];
$p->align = 'top';
$p->h = 30;
$p->text = 'Кнопка';
$p->on('click', function(){
UIDialog::message('Примет', 'Заголовок');
});
$form->add($p);

$form->on('windowClosing', function(){
System::halt(0);
});
});


Многопоточный HTTP сервер?




На JPHP можно вполне написать многопоточный http сервер. Для этого я импортировал классы Socket и ServerSokect, а также классы потоков Thread.

use php\concurrent\ExecutorService;
use php\io\IOException;
use php\lang\Environment;
use php\net\ServerSocket;

$server = new ServerSocket();
$server->bind('localhost', 8080);
$service = ExecutorService::newFixedThreadPool(5); // create a thread pool

echo "Start HTTP Server on http://localhost:8080/ ...\n";

while (true) {
$socket = $server->accept();
echo "Connect client ... \n";

$service->execute(function() use ($socket) { // processing a http request in a thread
ob_implicit_flush(true);

$out = $socket->getOutput();
try {
$out->write("HTTP/1.1 200 OK\r\n");
$out->write("Content-Type: text/html\r\n");
$out->write("Connection: close\r\n\r\n");

$out->write("Hello world!");
$out->flush();
} catch (IOException $e) {
echo "Error: " . $e->getMessage(), "\n";
} finally { // JPHP supports `finally` as in PHP 5.5
$socket->shutdownOutput();
}

}, new Environment());
}


Такой сервер довольно быстро отдает контент, я тестировал через утилиту ab и результаты впечатляют. На моей машине (Java 7, i3, Windows 7) такой сервер в состоянии обрабатывать 4000-5000 запросов в секунду (ab -n50000 -c100 http://localhost/) и не падать.


Что дальше?




Я планирую развивать сам JPHP, довести его до релиза, реализовать все языковые возможности до PHP 5.5. Возможно попробую реализовать совместимость с Android (по примеру Roboto для JRuby). Реализую нормальные расширения, основанные на namespaces и ООП, частично я кое что уже реализовал — потоки, сокеты, gui, json.

Остальное смотрите в презентации. Благодарю за внимание.



This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий