hnwの日記

Javaで書かれたPHP処理系のJPHPが高速だった

JPHPというJavaで書かれたPHP処理系があります。これは2013年10月にはじまった新しいプロジェクトで、大半をメイン開発者一人で開発しているようですが、既に一定以上の完成度です。また、内部的にはPHPファイルを解釈して直接Javaバイトコードコンパイルしており、かなり高速なPHP処理系になっています。


他のPHP処理系と異なり、既存のPHPコードをWebサーバ上で動かすことを重視していないようにも見えますが、興味深いプロジェクトですので紹介します。

JPHPを動かしてみる

さっそくJPHPを試してみましょう。現時点ではGitHubからソースコードを持ってきてビルドする必要があります。JDK7以上が必要ですので注意してください。JPHPのドキュメント「Getting Started」の通り、次のようにしてJPHPがビルドできます。

$ git clone https://github.com/jphp-compiler/jphp.git
$ cd jphp
$ chmod +x gradlew
$ ./gradlew dist


ビルド後、次のようにすればJPHPが実行できます。

$ build/dist/jphp -f jphp-example-project/src/main/php/bootstrap.php

Hello World!

$

ベンチマークテスト

PHPソースコード付属のベンチマークテストZend/bench.phpを動かしてみました。ただし、このbench.phpのテストのうちmandel()だけは無限ループしてしまうようだったので、コメントアウトして実行しました。

PHP7 PHP 5.6 Phalanger Quercus JPHP
simple 0.094 0.112 0.069 0.195 0.058
simplecall 0.028 0.116 0.019 0.179 0.009
simpleucall 0.054 0.112 0.021 0.212 0.061
simpleudcall 0.053 0.116 0.021 0.229 0.075
mandel2 0.347 0.356 0.584 0.769 0.315
ackermann(7) 0.078 0.140 0.044 0.264 0.132
ary(50000) 0.008 0.023 0.025 0.097 0.073
ary2(50000) 0.008 0.019 0.017 0.044 0.049
ary3(2000) 0.136 0.152 0.338 0.394 0.138
fibo(30) 0.181 0.374 0.151 0.609 0.183
hash1(50000) 0.017 0.029 0.116 0.114 0.077
hash2(500) 0.017 0.035 0.073 0.062 0.105
heapsort(20000) 0.067 0.092 0.116 0.310 0.216
matrix(20) 0.069 0.086 0.136 0.164 0.100
nestedloop(12) 0.156 0.199 0.115 0.256 0.076
sieve(30) 0.041 0.089 0.114 0.151 0.089
strcat(200000) 0.010 0.013 0.013 0.041 0.033
Total 1.363 2.061 1.969 4.089 1.787


JPHPはPHP7より遅いものの、PHP5.6/Phalangerと互角もしくは僅かに速いという結果になりました。ところで、上の結果はJavaバイトコードへのコンパイルJITコンパイルの時間が含まれています。Webサーバ上での動作を想定すると、このような処理は事前に行うことができますから、真の実力を測る意味でベンチマーク関数を2回呼んだ上で2回目の実行時間だけを測定してみました。

PHP7 PHP 5.6 Phalanger Quercus JPHP
simple 0.090 0.109 0.061 0.128 0.026
simplecall 0.027 0.117 0.018 0.125 0.004
simpleucall 0.050 0.115 0.019 0.170 0.051
simpleudcall 0.052 0.113 0.019 0.235 0.036
mandel2 0.336 0.336 0.575 0.770 0.205
ackermann(7) 0.074 0.133 0.041 0.208 0.113
ary(50000) 0.008 0.020 0.018 0.015 0.022
ary2(50000) 0.006 0.018 0.020 0.020 0.033
ary3(2000) 0.112 0.165 0.332 0.362 0.156
fibo(30) 0.179 0.349 0.147 0.524 0.185
hash1(50000) 0.017 0.030 0.116 0.034 0.035
hash2(500) 0.016 0.033 0.066 0.047 0.038
heapsort(20000) 0.067 0.087 0.105 0.180 0.116
matrix(20) 0.069 0.083 0.127 0.131 0.068
nestedloop(12) 0.158 0.190 0.108 0.244 0.082
sieve(30) 0.040 0.088 0.106 0.111 0.038
strcat(200000) 0.010 0.013 0.012 0.018 0.006
Total 1.309 2.000 1.891 3.321 1.213


JPHPが全テストの合計で見ると一番良い結果になりました。mandel2の寄与が大きいので単純に最速とは言えませんが、PHP5.6/Phalanger/Quercusとの比較では高速だと言えそうです。

互換性

JPHPは名前空間や配列の短縮構文、traitやgotoなど新しめのPHPの機能を実装していますが、PHP標準関数については未実装のものが多い印象です。既存のPHPコードを動かそうと思うとまだまだ苦労するでしょう。ちなみに、僕は上のZend/bench.phpを動かすために4個のPull Requestを投げました。


また、現時点でのJPHPで「==」で文字列同士を比較すると常に文字列として比較され、数値文字列同士の比較は考慮されていません。ドキュメントにもPHPやHHVMを置き換えるものではないと書いてあるなど、中の人が互換性にさほどこだわりが無いのかもしれません。

JPHPの狙い

ところで、JPHPではSwingを使ったGUIプログラミングができます。たとえば次のようなコードを実行すると各種OS上で新規ウィンドウが開きます。

<?php
use php\lang\System;
use php\swing\SwingUtilities;
use php\swing\UIForm;
use php\swing\UIManager;
use php\swing\UIButton;

UIManager::setLookAndFeel(UIManager::getSystemLookAndFeel());

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

        $button = new UIButton();
        $button->size = [300, 40];
        $button->position = [100, 100];
        $button->text = 'close';
        $form->add($button);

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

        $form->visible = true;
    });


このように、PHP文法でWebサーバ以外で使われるプログラムを書くこともJPHPの狙いの一つのようです。他にもデーモンを立ててTCP接続を待ち受けるような使い方や、Androidのコードを書くなどもできるようです。

実装を確認する

JPHPのレキサ・パーサは手書きです(jphp-core/src/org/develnext/jphp/core/tokenizer/ および jphp-core/src/org/develnext/jphp/core/syntax/ 以下)。実装力がある人にとっては既存のパーサジェネレータを覚える方がコストなのかもしれないですが、プロジェクト外の人がクイックハックしたいような場合には逆にコストが上がるので、善し悪しある部分かと思います。とはいえ、僕は今回そのあたりのソースコードも触りましたが、初見でも把握できる程度には整理されている印象でした。


Javaバイトコードの生成にはASMというライブラリを使っています。jphp-core/src/org/develnext/jphp/core/compiler/jvm/ 以下で構文木からバイトコード生成を行っており、この部分だけで7000行ほどの分量になっています。Javaバイトコードを自分で組み立てるというのは知識の意味でも根性の意味でもハードルの高い作業だと思うんですけど、メイン開発者が余暇に一人で書いたということなんでしょうか。世界は広いですね。

まとめ

Javaで書かれたPHP実装のJPHPを紹介しました。PHPソースコードからJavaバイトコードコンパイルするような実装になっており、ベンチマークテスト結果によれば他の実装と比べても高速な処理系であることがわかりました。JPHPがJVMの実力を十分引き出した結果だといえるでしょう。


JPHPのメイン開発者のDmitriy Zayceffさんはロシア人で、現状ではロシア語の資料の方が多いくらいの状況ですから、OSSとして見ると知名度向上が最初の課題になりそうです。商用環境で使えるようになる日は遠いかもしれませんが、趣味で触る分には面白い処理系だと感じました。


ちなみに、作者の方が忙しいのかここ数ヶ月ほど開発が止まっているようですが、Pull Requestの返事は超スピードで来ました。読者の方もJVMの勉強がてら触ってみてはいかがでしょうか。