【PHP】PHP8から導入予定の”JIT”について

PHPは現在で7.4が最新バージョンですが、2020年末にPHP8の公開が予定されているようす。PHP 8で実装される予定の機能に、JITというものがあるので、取り上げてみたいと思います。

JITとは

Just-In-Time の略で、プログラムの実行時に、あらかじめ用意された中間コードを保持しておき、それを使いまわすことでパフォーマンスを向上させようとする仕組みのことです。

PHPは現在は、アクセスが来るたびにソースコードを読み取り、opcodeに変換して、順番に逐次実行するというインタプリタ型のプログラミング言語です。
OPCacheはこのうち、opcodeが生成されたタイミングで、それをメモリに保持し、同じopecodeを生成しないようにしてパフォーマンスを改善していますが、JITはもう一段階進んだもので、リクエストが来たらソースコードを読んでopcodeにするまでは同じですが、その後一気にネイティブコードにまで変換し、その結果をメモリに保持します。そのためネイティブコードを使いまわせるので大幅なパフォーマンスの向上が可能なようです。

php.net の RFC:JIT に次のようにあります。

他の最適化戦略を使用してPHPのパフォーマンスを向上させることは、限界に到達したと考えています。つまり、JITを使用しない限り、PHPのパフォーマンスをさらに向上させることはできません。
~中略~
C言語ではなく、もしくはC言語のかわりに、PHPで組み込み関数を開発することが可能になります。
現在のPHPでそのような戦略を採るには大きな壁となっている、パフォーマンス劣化という問題の影響をほとんど受けなくなります。
さらにPHPベースで開発すれば、C言語ベースでの開発では往々にして発生するメモリ管理、オーバーフローといった問題を、言語レベルで安全にしてくれます。(php.net PHP RFC:JIT

コンパイル型の言語であるCに代わる選択肢にもなりうるのであれば、パフォーマンスに期待できそうです。

パフォーマンス

具体的にJITが導入されるとどれくらいのパフォーマンスが向上されるかですが、以下の関数についてのベンチマークが JIT:RFC に公開されています。

    function iterate($x,$y)
    {
        $cr = $y-0.5;
        $ci = $x;
        $zr = 0.0;
        $zi = 0.0;
        $i = 0;
        while (true) {
            $i++;
            $temp = $zr * $zi;
            $zr2 = $zr * $zr;
            $zi2 = $zi * $zi;
            $zr = $zr2 - $zi2 + $cr;
            $zi = $temp + $temp + $ci;
            if ($zi2 + $zr2 > BAILOUT)
                return $i;
            if ($i > MAX_ITERATIONS)
                return 0;
        }
 
    }

以下の表は、こちらに公開されている、PHP7でのJITのバフォーマンスと、その他のJIT実装のパフォーマンスのベンチマークをまとめたものです。

環境JIT-offJIT-on
PHP7 0.2810.011
HHVM-3.5.00.9780.030
gcc0.0130.022
Java-1.8.00.2510.059


PHP7と比較してJIT有効でおよそ20倍のパフォーマンス向上があるようです。ただ、2015年2月に測定されたベンチマークなので、実際のパフォーマンスとは異なるかもしれません。


後方互換性について

互換性の壊れる変更は無いようですが、一部のデバッガやプロファイラへの影響があるようです。


その他のPHP8で導入予定の機能

その他、気になったPHP8で導入予定の機能について紹介します。

ユニオン型

ユニオン型とは、通常は1つしか指定できない「int」や「string」といった変数の型を複数指定できる機能です。ユニオン型を使用する際は下記のようにコードを記述します。

public function foo(Foo|Bar $input)

注意点として、返り値がないことを表すvoid型はユニオン型に含めることはできません。


オブジェクトのWeakMap

PHP 7.4で弱参照と呼ばれる機能が実装されました。通常、メモリ領域を自動で解放するガベージコレクタは、オブジェクトが参照されている場合はそのオブジェクトを解放することができませんが、弱参照によって参照されたオブジェクトは、ガベージコレクタによる解放の対象となります。PHP 8で実装予定のWeakMapをオブジェクトに対し宣言しておけば、そのオブジェクトが参照されていてもガベージコレクタによる解放の対象とすることができます。

class Foo 
{
    private WeakMap $cache;
 
    public function getSomethingWithCaching(object $obj): object
    {
        return $this->cache[$obj]
           ??= $this->computeSomethingExpensive($obj);
    }
}

::classでクラス名を取得

オブジェクトのクラス名を取得するのに、PHP 8からはget_class()だけでなく::classが使用できるようになります。

$foo = new Foo();
var_dump($foo::class);



参考

php.net RFC:JIT
php.net RFC


知識・ノウハウ共有
56件
ひさだ
2020.03.03

PHP7.5でも変数に型指定ができるようになるらしいく
だんだんPHPもJavaっぽくなってきますね!

twitterで見たのですが、
言語的にも静的実装の風潮が最近はあるらしいです。

WeakMapとかの利用ケースとか注意点とかがあるとありがたいです。

川崎 誠士
2020.03.04

>言語的にも静的実装の風潮が最近はあるらしいです。
そうなのですね。型が明示されている方が、開発もしやすいのでしょうか。
動的言語で静的な実装もできるのなら、両者良いとこどりができないかなぁとは思いました。

>WeakMapとかの利用ケースとか注意点とかがあるとありがたいです。
ありがとうございます。確かに実際に使うとなるとそういった点が必要になりますよね。
調べてみようと思います!

関連記事