PHP8で追加されたJITと、PHP OPcacheについて徹底解説！「悪りぃが、こっから先は一方通行だ」

## はじめに
PHPerの皆さんこんにちは。[一方通行](https://ja.wikipedia.org/wiki/%E4%B8%80%E6%96%B9%E9%80%9A%E8%A1%8C_(%E3%81%A8%E3%81%82%E3%82%8B%E9%AD%94%E8%A1%93%E3%81%AE%E7%A6%81%E6%9B%B8%E7%9B%AE%E9%8C%B2))と申します。
皆さんは去年(2020年)の11月にPHP 8がリリースされたのは既知のことかとは思いますが、
PHP 8の最も目玉である機能に[「JIT (ジャストインタイム) コンパイル」](https://www.php.net/releases/8.0/ja.php)というものがあります。

JITをPHPアプリケーションに導入しようか検討する際にJITが内部的に何を行っているのかきちんと理解していなければ導入出来ないと思い、
別の高速化アプローチであるOPcacheと合わせて、解説します。

雰囲気は知っているよという方や、OPcache is 何?という方々はぜひご参考にしてください。

## 前提知識
まずですが、インタプリタ言語がマシン上でどのように実行されているかおさらいしましょう。
大まかな流れは下図のとおりとなります。
![qiita_php8_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170923/74d830bc-0a2a-2501-ccb7-5c8890650732.png)

このようにソースコードを元に[機械語](https://ja.wikipedia.org/wiki/%E6%A9%9F%E6%A2%B0%E8%AA%9E)まで変換するプログラムを[インタプリタ](https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%97%E3%83%AA%E3%82%BF)と呼びます。
PHP及びRubyやPythonのインタプリタはC言語で書かれています。

これらを踏まえた上で、説明していきます。

## PHPの実行フローをおさらい

PHPの実行フローは下図のとおりです。
![qiita_php8_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170923/1a01e6fe-9f6c-1a13-1458-16273201ee94.png)
ソースコードから[Opcode](https://ja.wikipedia.org/wiki/%E3%82%AA%E3%83%9A%E3%82%B3%E3%83%BC%E3%83%89)へ変換し、変換されたOpcodeをZend Engineが解釈し機械語へ変換していきます。

### Opcode is 何?

ここで、新たにOpcodeという単語が出てきましたが、これは最初の図にある[バイトコード](https://ja.wikipedia.org/wiki/%E3%83%90%E3%82%A4%E3%83%88%E3%82%B3%E3%83%BC%E3%83%89)と同義です。
Opcodeとは仮想マシン(Zend VM)で実行できる[疑似命令](https://www.weblio.jp/content/%E6%93%AC%E4%BC%BC%E5%91%BD%E4%BB%A4)で、Opcodeは最終的に配列になり順次VMを介して実行されていきます。

以下のtest.phpをDebugした結果が、更に下のterminalのキャプチャで、
0000~0004までがOpcodeとなります。

``` php:test.php
<?php
$accelerator = '悪りぃが、';
echo $accelerator . 'こっから先は一方通行だ';

echo PHP_EOL;
```


![スクリーンショット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170923/eb7ad1d2-65d0-7570-d0e2-7380fc96a001.png)

Opcodeの処理内容を確認するには[ここ](https://www.php.net/manual/ro/internals2.opcodes.php)から確認できます。
(※ LinkはZend Engine 2のものです)

### Zend Engine is 何?
Zend Engine(VM)はソースコードを中間表現(Opcode)に変換し、中間表現を実行します。

実行の度にソースコードを解析し機械語にコンパイルしていくより、
一旦ファイル単位でOpcodeへ変換しそれを実行する方がより高速でありリソース管理の面から優れているためZend VMが導入されました。

```php -v```を実行した際にZend Engineのバージョンとコピーライトが出てくるかと思いますがそれです。

## OPcache is 何？

Opcodeを共有メモリに保存しておくことを指します。
つまり、OPcacheはPHPの[アクセラレータ](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%82%AF%E3%82%BB%E3%83%A9%E3%83%AC%E3%83%BC%E3%82%BF)と言えます。
図に表すと以下のとおりです。
![qiita_php8_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170923/51669b62-e1b2-08ef-4ae8-e4f5683e0d13.png)

## JIT is 何?

厳密な意味合いを言うと、VMの「バイトコード」を「ネイティブコード(機械語)」にコンパイルすることを指します。
そしてJITはコンパイルの際、ネイティブコード(機械語)のキャッシュを行います。

キャッシュが存在する場合はZend VMを挟む必要がなくなり、プロセッサレベルの命令として直接機械語の実行が可能となります。
![qiita_php8_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170923/5fb4ed44-6cd1-2292-7a68-7638973428f6.png)


## PHP JITの種類

### Function JIT

Function JITは関数単位でJITコンパイルが行われます。
問題は関数単位で全てJITコンパイルするため、場合によってはパフォーマンスが低下する恐れがあるということです。

### Tracing JIT
Tracing JITはランタイム情報を含んだスタックトレースを作成し、トレースの回数や内容を元にJITコンパイルするかを判別してくれます。(ホットコードを識別して最適化)
![qiita_php8_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170923/26bee0f8-bbb5-b61f-15a4-cf0ed12e0adb.png)
### カスタマイズ
4桁の整数値 CRTO を入力することで、JITのトリガーや最適化レベルを変更できます。
詳しくは[こちら](https://www.php.net/manual/ja/opcache.configuration.php#ini.opcache.jit)を参照ください。


## あとがき
PHP 8で導入されたJITは、簡素に説明してしまえば「OPcacheより高速化が見込めるキャッシュ機能」と言えるわけですが、
実際はOPcacheとは切り離して考えることは出来ず、インタプリタの実行フローを把握していないと全貌を捉えることが出来ませんでした。

また、記事執筆にあたり新たな発見があり自分的にも書いてよかったと思っています。

PHPは言語仕様がちょっとアレなところはありますが、なんでも取り込む貪欲な姿勢からは今後も目が話せませんね。

ここまで読んで頂きありがとうございました！

## 採用PR

https://hrmos.co/pages/vivion/jobs/0000226

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/307359/2000f749-3e82-7a47-b1c4-4444e237f79c.png" width=45%>