いつの間にやらZephirのバージョンが0.5に上がっていました。
Zephirで開発されているphalconのほうもこの間とうとうベータ2になったようです。
PHP5.6のリリースやJITコンパイラの実装計画、hackの登場など今年のPHP界隈は色々と話題を欠かさないですね。
(最近のPHP本体のアップデートにCVE系のアップデートが多いのが気になりますが。。。)
そんな盛り上がりを見せる中、PHPカンファレンス2014の開催ももうすぐということで、
登壇する代わりにQiitaにZephirの話を載せてPHPの盛り上がりに火をくべることにしましょう。
What's Zephir ?
Zephirが何か?についてはQiitaの以下の投稿を見ればどんなものなのかは理解出来るでしょう。
ZephirはどのようにしてPHPエクステンションを作るのか
先に挙げたリンクを読めばZephir使えば未知の世界だったエクステンションつくるのとか楽勝!!
と思うようになっていただけると思います。
でも、作れるだけではなくてどんなふうに作られているのかまで知っているのがエンジニアってやつですよね。
ZephirのコンパイラはPHPで出来ています
もしかすると、PHPがテンプレートエンジンだと信じて疑わない人には何を言っているのか理解できないかもしれません。
あるいは、何でも適材適所と言い切ってPHPの可能性を信じていない人(実際に適材適所はあると思いますが;)にも全く理解できないかもしれません。
ZephirのコンパイラはPHPで出来ています
ちょっと大袈裟に書きましたが、PHPがコンパイルして出力するのはCのコードであってバイナリではありません。
coffescriptのコンパイラやstylusのコンパイラをPHPで実装したライブラリが幾つか存在していることを考えればどうということはないでしょう。
(TypeScriptというAltJsな言語だってコンパイラはjavascriptで書かれていますしね。)
ZephirはPHPのコンパイラによってCのコードにコンパイルされ、
そこからさらにgcc
によってコンパイルされてエクステンションに仕上がっているだけなのです。
PHPでコンパイルされるのでコンパイル速度に期待してはいけません。
コードを見てみる
サンプルコードからZephirがどのようなCのコードを生成するのか見てみましょう。
namespace Test;
class Test
{
public static function inArray(var needle, array arr)
{
return in_array(needle, arr);
}
}
このコードをコンパイルすると、ext
フォルダの中にnamespaceと同じ名前のフォルダが生成されているのが確認出来ます(今回はtest)。
greet.zep.c
とgreet.zep.h
というC言語だと思わずにはいられないファイルができていることが確認出来ます(実際に中はC言語です)。
ext/
.
.
.
test/
.libs/
test.lo
test.zep.c
test.zep.h
コンパイルされたCのコードはこんな感じになります。
#ifdef HAVE_CONFIG_H
#include "../ext_config.h"
#endif
#include <php.h>
#include "../php_ext.h"
#include "../ext.h"
#include <Zend/zend_operators.h>
#include <Zend/zend_exceptions.h>
#include <Zend/zend_interfaces.h>
#include "kernel/main.h"
#include "kernel/array.h"
#include "kernel/operators.h"
#include "kernel/memory.h"
ZEPHIR_INIT_CLASS(Test_Test) {
ZEPHIR_REGISTER_CLASS(Test, Test, test, test, test_test_method_entry, 0);
return SUCCESS;
}
PHP_METHOD(Test_Test, inArray) {
zval *arr = NULL;
zval *needle, *arr_param = NULL;
ZEPHIR_MM_GROW();
zephir_fetch_params(1, 2, 0, &needle, &arr_param);
zephir_get_arrval(arr, arr_param);
RETURN_MM_BOOL(zephir_fast_in_array(needle, arr TSRMLS_CC));
}
Zephirはエクステンションを作るにあたってコードを最適化してくれる
さて、感の良い人やコード読むのが大好きな人は先に載せたコンパイル後のコードを見ておや?と感じたはずです。
元のZephirのコードは引数を受け取って中でin_array
を呼び出しているだけなので、
コンパイル後のコードはPHP本体のin_array
実装で利用されているphp_search_array
関数が呼び出されていればいいはずです。
しかし、コンパイルされたZephirでは最終的にzephir_fast_in_array
という関数が呼ばれていることが分かります。
Zephirからphpの標準関数と同じ名前の関数を呼ぶ処理を書いた場合、コンパイル時にZephirが提供する最適化されたphpの標準関数と同等の機能を持つ関数が呼ばれるようになるのです。
この仕組によってZephirはプログラムのパフォーマンスを向上させることを実現しているのです。
(最も、これは速度向上のための一側面でしかありませんが…)
速度を比較してみよう
先に載せたinArray
とphpのin_array
を使って本当にパフォーマンスが向上しているのか確認してみましょう。
phpのin_array
といえば計算量がO(n)になることで悪評が高いですね。
1~1000までの配列から値を探す処理を書いて3つのパターンで処理速度を比較してみます。
function measure($search)
{
$start = microtime(true);
$array = range(1, 1000);
foreach (range(1,10000) as $i) {
in_array($search, $array);
}
var_dump(microtime(true) - $start);
}
measure(1); // 1~1000の配列から1を探す
measure(500); // 1~1000の配列から500を探す
measure(0); // 1~1000の配列から0を探す(見つからない)
function measure($search)
{
$start = microtime(true);
$array = range(1, 1000);
foreach (range(1,10000) as $i) {
\Test\Test::inArray(1, $array);
}
var_dump(microtime(true) - $start);
}
measure(1); // 1~1000の配列から1を探す
measure(500); // 1~1000の配列から500を探す
measure(0); // 1~1000の配列から0を探す(見つからない)
以下のような結果になりました。
普通のin_array | Zephirのin_array | |
---|---|---|
1~1000の配列から1を探す | 0.0066111087799072 | 0.0051839351654053 |
1~1000の配列から500を探す | 0.077008008956909 | 0.051916122436523 |
1~1000の配列から0を探す(見つからない) | 0.14873790740967 | 0.096560001373291 |
全てのケースにおいてZephirのin_arrayのほうが高速に動作することが確認出来ます。
また、1~1000の配列から500を探す、__1~1000の配列から0を探す(見つからない)__ケースのように配列の後ろの方の値を探す場合より高速に動作していることが確認出来ます。
補足とか
- zephir0.5が出たのにバージョン0.4.6で書いてるのは0.5だとスケルトンを生成した時にコンパイルに必要な情報を書く
config.json
が作成されなくなっていたためです。(どう書いていいのか調べるの面倒だった) - やらたコンパイラという単語が飛び交いますが、本文中でのコンパイラは何かを変換して別の何かを出力するものとして読み進めて下さい。
- 冒頭でphpカンファレンス2014に登壇する変わりなどと宣っていますが、私自身にはそれほどの実績も知識もございません、申し訳ありません。
- Zephirがパフォーマンスを向上させるのはコードの最適化だけでなく、エクステンションにコードをまとめることで
require
の数を削減出来ることにもあります。
おまけ
Zephirの公式サイトにはSublimeText
とTextMate
にSyntaxハイライト提供されていると書いてあります。
実はintelliJ(多分phpstormもあるはず)にもzephirのプラグインがあります(今のところSyntaxハイライトされるぐらい)。