hnwの日記

MacOSX上で.NET環境向けPHPコンパイラPhalangerを動かしてみた


(1/7 追記ベンチマークテスト結果が不正確だったので、再測定して書き直しました。以前の記事よりもPHPの結果が良くなっています。


あけましておめでとうございます。このたび、gihyo.jpの新春特別企画として記事「PHP処理系の未来」を寄稿いたしました。HHVMとPHP7を絡めて最近のPHPの動向を紹介するような内容になっていますので、是非ご覧くださいませ。


この記事を書くにあたり、HHVM以外にも有望なPHP処理系があるかどうかを調べていました。その結果、HHVMほどではないにせよ実用レベルに近いPHP処理系が複数あることがわかりました。


そうした実装のうち、今回は.NET FrameworkおよびMono環境で動くPHPコンパイラPhalangerを紹介します。PhalangerはC#で書かれており、PHPソースコードから.NETアセンブリを出力します。Windows上では本家PHPより高速ということですし、PHPをVisualStudioで開発したり.NETの資産を利用したりする狙いもあるようで、夢が広がるプロジェクトですね。Phalanger開発者がDEVSENSE社という会社を起業して有償サポートを提供しているなど、本気度が高いPHP処理系の一つだと言えるでしょう。


ただ、中の人も含め常用している人は全員Windows環境のようで、Linux/MacOSXの情報は非常に少ない状況です。今回MacOSXのMono環境で動かせたので紹介します。

Monoのインストール

まずはMonoを準備してください。PhalangerではMonoの設定ファイルを書き換える必要があるので、普段使っているMonoとは別にインストールした方が無難かと思います。普段Monoを使っていない場合は単にHomebrewでインストールすれば良いでしょう。

$ brew install mono

Phalangerのインストール

PhalangerにはMacOSX用のパッケージはありません。Windows用のバイナリファイルをダウンロード&展開します。僕はPhalangerのダウンロードページから「Phalanger 3.0.0.4072 (bin).zip」をダウンロードしました。


これを作業ディレクトリに展開し、「Installation/3.0/Linux - PhalangerWiki」を参考に作ったインストールスクリプトでインストールします。インストールスクリプト内でxmlstarletを使っているので、必要に応じてインストールしてください。

$ mkdir /tmp/phalanger-setup
$ cd /tmp/phalanger-setup
$ unzip -x "$HOME/Downloads/Phalanger 3.0.0.4072 (bin).zip"
$ brew install xmlstarlet
$ curl -o phalanger_install_3.0.sh https://gist.githubusercontent.com/hnw/3d3b280bdd9b6a3835b8/raw/3068f3ef31d4a6b0ae88c455d526b790250f7ea3/phalanger_install_3.0.sh
$ bash ./phalanger_install_3.0.sh


トラブったらスクリプトの中身を熟読してください。僕自身、Mono力が足りないため中身を全部把握しているわけではありません。

PHPプログラムのコンパイル&実行

Phalangerのコンパイラはphpc.exeです。下記のようにしてPHPプログラムをコンパイルします。

$ mono /usr/local/lib/phalanger/bin/phpc.exe /out:/tmp/ bench.php


これでコンパイル結果が/tmp/bench.exeに出力されます。/outオプション無しだとカレントディレクトリのbin/以下にバイナリを作るので注意してください。


できたexeファイルはやはりmonoで動かせます。

$ mono /tmp/bench.exe


ちなみに今回はPHPファイルからexeファイルを作りましたが、dllファイルで出力してApache+Mono環境で動かすこともできるようです。

ベンチマークテスト

せっかくなので、PHPとPhalangerの速度を比較してみましょう。PHPソースコード付属のベンチマークテストZend/bench.phpMacOSX 10.9上で実行してみました。

PHP7 PHP 5.6 Phalanger
simple 0.094 0.112 0.069
simplecall 0.028 0.116 0.019
simpleucall 0.054 0.112 0.021
simpleudcall 0.053 0.116 0.021
mandel 0.314 0.304 0.387
mandel2 0.347 0.356 0.584
ackermann(7) 0.078 0.140 0.044
ary(50000) 0.008 0.023 0.025
ary2(50000) 0.008 0.019 0.017
ary3(2000) 0.136 0.152 0.338
fibo(30) 0.181 0.374 0.151
hash1(50000) 0.017 0.029 0.116
hash2(500) 0.017 0.035 0.073
heapsort(20000) 0.067 0.092 0.116
matrix(20) 0.069 0.086 0.136
nestedloop(12) 0.156 0.199 0.115
sieve(30) 0.041 0.089 0.114
strcat(200000) 0.010 0.013 0.013
Total 1.677 2.365 2.356


判断しにくい結果になりましたが、トータルで見るとPhalangerとPHP 5.6が同程度、PHP 7との比較だと約1.5倍遅いという結果になりました。とはいえテストによってはPHP7より断然速いものもありますし、Phalangerのメインターゲットである.NET Framework環境では更に高速になるのかもしれません。十分な可能性を持った実装だと言えるでしょう。

互換性

本家のWikiPhalangerとPHPとの互換性に関する情報がまとめられていますが、言語自体もエクステンションもかなり頑張って追従しているようです。SPL、リフレクション、traitあたりに積み残しがあるものの、それ以外はPHP 5.4まで追いついているようです。


IIS+SQL Server+Phalanger+WordPressを動かしたという記事も見つかりました(参照:「WordPress on .NET with SQL Server is Possimpible using Phalanger 3.0」)。なかなかの変態構成で良いですね。


HHVMと違ってPHPからCソースコードをもらってくることができず、自前実装部分が増えがちなのは不安点ではありますが、実装力(というか根気)でカバーしているようです。たとえば==のキモい挙動もかなり再現できていました(InfやNaNあたりの挙動が違っている程度で、トラブルの原因にはならないと思います)。

実装をチラ見する

PhalangerのソースコードGitHubで公開されています(参照:DEVSENSE/Phalanger)。これを眺めてみました。


パーサーはGPPGを利用しています。Source/Core.Parsers/Generators/以下を見ると、Lexer.lexとParser.yはPHP本体のものを修正して使っているようです。


演算子や組み込み関数の実装などは完全に新規で書いているようです。==の比較に相当する分だけでも長々とC#コードが記述してあって、真似したくはないけど感心しますね(参照:Source/Core/Comparisons.cs)。

まとめ

(少なくとも日本では)あまり知名度がなくても真面目に作り込まれてるPHP処理系ってあるもんですね。LL言語を採用するような現場でWindows Serverに詳しい人がいるのか?という点がこの手の.NET実装に共通する疑問ではありますが、海外では状況が違うのかもしれません。