hnwの日記

PHPを高速化するRecki-CTとは一体何なのか

@ircmaxellさん作の「Recki-CT」を試してみたので紹介します。作者によるブログ記事「ircmaxell's blog: Introducing Recki-CT」によれば処理によってはHHVMやHippy VMより速いらしいのですが、この記事だけでは詳細がよくわからずモヤモヤしていました。今回ソースコードまで確認してみて、前作のPHPPHPを超える変態作品だとわかりました(参照:「PHPPHPがド変態であることに今さら気づいた」)。

Recki-CTを試してみる

Recki-CTの概要を説明すると、既存のPHP関数に型情報のアノテーションを記述して呼び出し方を少し変えることで、その関数をJITコンパイルしてネイティブコードとして実行するPHPライブラリです。JITコンパイルにはlibjitのPHPバインディングであるJIT-Fuを利用しています。


では、さっそく試してみましょう。次のコード中のgcd関数をJITコンパイルで高速化したいとします。

<?php
function gcd($x, $y)
{
    if ($x === $y) {
        return $x;
    } else if ($x < $y) {
        return gcd($x, $y - $x);
    } else {
        return gcd($x - $y, $y);
    }
}

$z = gcd(407074349875, 691666152);


これに対してRecki-CTを適用すると次のようなコードになります。

<?php
require_once __DIR__ . '/vendor/autoload.php';

use ReckiCT\Jit;

/**
 * @param int $x
 * @param int $y
 * @return int
 */
function gcd($x, $y)
{
    if ($x === $y) {
        return $x;
    } else if ($x < $y) {
        return gcd($x, $y - $x);
    } else {
        return gcd($x - $y, $y);
    }
}

$gcd = Jit::JitFu('gcd');
$z = $gcd(407074349875, 691666152);


このように、十分少ない変更で既存の関数gcdをJITコンパイル対応にできます。既存の関数をJIT-Fuに書き換えるのはかなり面倒なので、これだけでも面白い成果だと言えるでしょう(参照:「PHPのlibjitバインディングであるJIT-Fuを試してみた」)。

Recki-CTの中身

上の挙動だけ見ると、Recki-CTはPHPからJIT-Fuへのトランスレータなのかな、と考えてしまいます。しかし、Recki-CTはそれよりずっと野心的なプロジェクトです。Recki-CTの処理は次のようになっています。


僕自身はコンパイラに詳しいわけではないのですが、真面目なコンパイラ実装と同様の処理を行っていると考えてよさそうです。


また、上の処理で中間表現(IR)を作るところまではJIT-Fuと全く無関係に動作します。言い方を変えると、コンパイラとしてのRecki-CTにとってJIT-Fuは対応アーキテクチャの一つでしかありません。今後JIT-Fu以外のJITコンパイラ実装が出てきても差し替えは容易ですし、PHPやその他の言語で出力することもできます。最適化フェーズで使える情報が多いため、他の方法ではできない最適化の可能性がある点も魅力ですね。

まとめ