preloadとJITの話をなるべくわかり易く、それでいて深く書いてみました。
@hanhan1978ソースコードから理解するPreloadとJITの話PHP Conference Japan 2020/12/12
View Slide
@hanhan1978● 富所 亮● 職業○ Webアプリケーションエンジニア○ 雑用係● ブログ○ https://blog.hanhans.net● Yokohama North AM○ https://anchor.fm/yokohama-north-am
興味のある方は...※気が向いた時にやってます ...https://shadow-php.connpass.com/
秒でGDBデバッグhttps://github.com/hanhan1978/shadow-php/wiki
本日のテーマpreloadとjitの肌感を掴む● 謎の技術にせず、仕組みから理解● どれくらい速度向上するのか● 本番環境での採用是非
PHP高速化の歴史
PHPはスクリプト言語実行の大まかな流れ
PHPはスクリプト言語実行の大まかな流れここで時間がかかる
PHPerが行ってきた解決策
中間コードキャッシュコンパイル結果(OPCode)をメモリにキャッシュ
歴代の中間コードキャッシュXCache 〜PHP5.6eAccelarator 〜PHP5.6Alternative PHP Cache (APC) PHP5.4Zend Opcache PHP5.5〜※これらのツールを同居させると ShareMemory戦国時代になりSegFault
OPCacheがデファクトスタンダードPHP5.5以降は一択そして、PHP7.4以降からOPCacheにさらなる高速化の仕組みが導入されるようになった....ここから本題
余談1稀によく見るうっかりさん。OPCache入ってない※php -v ですぐ確認できるので、心当たりのある方は確認を
※OPcacheが入ってなくて良いことは一個もないです
PHPスクリプト実行の詳細
PHPはスクリプト言語● 実行時にコンパイル● コンパイル&実行を繰返す
例えば
PHP実行の流れ
実行の詳細
中間コードキャッシュによってコンパイルの過程が省略される
具体例
ファイル構成
index.php
autoload.php
A.php
B.php
実行すると
実行するとautoloaderが2回呼び出されている
実行の流れ
4回のコンパイル
OPCacheが解決するもの4回分のコンパイルがキャッシュで解決
計測
OPCache − ◯Req / Sec 111.55 680.42※OPCacheの有効化によって、処理速度は約 6倍Laravelの30秒ベンチマーク
高速化技術が達成すること1. OPCache >>2. OPCache preload >>3. OPCache JIT >>?????????
高速化技術が達成すること1. OPCache >> コンパイル回数の軽減2. OPCache preload >>3. OPCache JIT >>??????
OPCache preloadとは?
PHP7.4から追加https://wiki.php.net/rfc/preload
● OPCacheの機能追加として提案● サーバー起動時に指定ファイルをコンパイルして、メモリに読込※一見すると、中間コードキャッシュと同じことをしているように見える
preload.php
php.ini
実行するとClass Aはautoloadされてない!!
3回のコンパイル
● autoloadが省略される● preload以外のファイルはopcache.validate_timestamp=0と同じ挙動● 直接のrequireはpreloadが活用されない
preloadソース v7.4.3
ext/opcache/ZendAccelarator.c- SAPI起動時に L4200 preload_load()- globalなデータ領域にpreloadしたクラス、ファイルの情報を登録する- opcodes実行時のコンパイルに使われるのは、L1914persistent_compile_file。これはpreloadの有無に依らない
Zend/zend_execute_API.c- L1419 zend_fetch_class_by_name が未解決のクラス名に対してコールされる- zend_hash_findでコンパイル済みのクラスが発見されれば、それを使う(preload)- それ以外は、autoloaderを使ってクラスの解決が行われて、ファイルが見つかればコンパイルされる※要するにpreloadされるとautoloaderまで処理がいかずに解決できる
Laravelの30秒ベンチマークOPCache − ◯ ◯Preload − − ◯Req / Sec 111.55 680.42 774.14※preload有効化によって、処理速度は約 14%向上
高速化技術が達成すること1. OPCache >> コンパイル回数の軽減2. OPCache preload >> autoloadの軽減3. OPCache JIT >> ???
余談Windows用PHPのpreload機能は7.4.2の時点で機能削除https://www.php.net/manual/en/opcache.preloading.php
OPCache JITとは?
PHP8.0から追加https://wiki.php.net/rfc/jit
ZendVMで実行するのではなくNative Codeを実行する
● 機械語実行により処理速度が最適化● 同様のことはpcre-jitやJS等でもおなじみ
OPCodeをさらに機械語に変換CPUで直接実行!!
● コード実行が最適化● JITコンパイルのオーバーヘッドは実行速度で補填
JITソース v8.0.0
OPCacheが主に関わってくるソースコードJIT関連はopcache/jitに固まっているので分かりやすい。
opcodeをx86のアセンブラに変換しているzend_jit_x86.c
ext/opcache/jit/zend_jit.c- zend_jit_op_array において泥臭い変換処理が行われている- アーキテクチャ依存のコード変換なので未対応だと動作しない- LuaJIT由来のDynAsmを利用
JIT化される単位- zend_jit_op_array はファイル毎に呼び出し- クラス単位、関数単位でopcodeの最適化が行われて、関数のIOを合わせた形でZendVMの主処理と繋ぎ合わされる- 1ファイル1関数に全部の処理を入れたら、効果的に効きそう
Laravelの30秒ベンチマークOPCache − ◯ ◯Preload − − ◯Req / Sec 111.55 680.42 774.14Req / Sec (JIT) − 696.57 812.93※JIT有効化によって、処理速度は約 2.5〜5%向上
モンテカルロ法を計測
円周率計算のベンチマークOPCache − ◯Sec 9.74 8.96Sec (JIT) − 5.20※JIT有効化によって、処理速度は約 42%向上100,000,000回試行の処理時間
高速化技術が達成すること1. OPCache >> コンパイル回数の軽減2. OPCache preload >> autoloadの軽減3. OPCache JIT >> コード実行の最適化
JITの現状
● https://bugs.php.net/search.php?cmd=display&package_name[]=opcache● RedditのPHP系板● https://www2.slideshare.net/nikita_ppv/justintime-compiler-in-php-8情報キャッチアップ
使用上の注意
● 内部の実行パスが変わる● 開発も同じ設定にするのが吉● ファイル更新でopcache_clearとか...
phpunitで使うにはこれを設定しないと、コマンドライン実行時にopcacheが動かない。
まとめ
高速化技術が解決すること1. OPCache >> コンパイル回数の軽減2. OPCache preload >> autoloadの軽減3. OPCache JIT >> コード実行の最適化
仕組みを理解適切なアプリケーションに適用コレ大事!!
参考1https://speakerdeck.com/hanhan1978/web-application-tuning-guildline
参考2https://gist.github.com/hellerbarde/2843375Network IOはメモリアクセスの5万倍遅い
リアルなウェブアプリのボトルネックは、大半がDBアクセスCPU負荷の数%は全体のボトルネックでは微々たるもの
本番投入はオススメしづらい...ISUCON用機能なのでは...