@require: option @require: class-slydifi/theme/akasaka @require: figbox/figbox let ex-big-textbox ?:size-opt it = let size = Option.from 48pt size-opt in FigBox.textbox?:(set-font-size size) it let big-textbox ?:size-opt it = let size = Option.from 32pt size-opt in FigBox.textbox?:(set-font-size size) it let mid-textbox ?:size-opt it = let size = Option.from 24pt size-opt in FigBox.textbox?:(set-font-size size) it let with-frame figbox = figbox |> FigBox.hvmargin 16pt |> FigBox.frame 2pt Color.black open FigBox in document '< +set-config(| SlydifiThemeAkasaka.default-config with color-emph = Color.black; |); +make-title(| title = { |来る新 JIT エンジンについて |知った気になる |}; author = {|nsfisis (いまむら)|}; date = {|PHPカンファレンス小田原 2024|}; |); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +frame{自己紹介}< +fig-center(vconcat [ gap 30pt; big-textbox{いまむら}; ]); +fig-center(vconcat [ ex-big-textbox{nsfisis}; ]); +fig-center(vconcat [ include-image 128pt `assets/me.jpeg`; ]); +fig-center(vconcat [ big-textbox{\@ デジタルサーカス株式会社}; ]); > %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +frame{はじめに}< +fig-center(vconcat [ gap 60pt; big-textbox{来る新 JIT エンジンについて} ]); +fig-center(vconcat [ gap 30pt; big-textbox{知った気になる} ]); > +frame{普通の PHP プログラム}< +fig-center(vconcat [ gap 30pt; mid-textbox{PHP スクリプト} |> with-frame; gap 30pt; hconcat [ mid-textbox{opcode} |> with-frame; gap 20pt; textbox{VM で実行} |> hvmargin 18pt; ]; ]); > +frame{普通の PHP プログラム}< +fig-center(vconcat [ gap 30pt; mid-textbox{PHP スクリプト} |> with-frame; gap 30pt; hconcat [ mid-textbox{opcode} |> with-frame; gap 20pt; textbox{VM で実行} |> hvmargin 18pt; ]; ]); +fig-center(vconcat [ gap 40pt; mid-textbox{もっと速くしたい}; gap 20pt; mid-textbox{機械語にして CPU で動かす}; ]); > +frame{JIT コンパイルとは}< +listing{ * Just In Time: ちょうど間に合って * 実行時に機械語へコンパイルする * PHP 8.0 で導入 * 他の処理系での事例 ** LuaJIT ** V8 TurboFan、Sparkplug、Maglev ** CRuby MJIT、YJIT、RJIT ** HotSpotVM ** GraalVM ** など } > +frame{普通の PHP プログラム}< +fig-center(vconcat [ gap 30pt; mid-textbox{PHP スクリプト} |> with-frame; gap 30pt; hconcat [ mid-textbox{opcode} |> with-frame; gap 20pt; textbox{VM で実行} |> hvmargin 18pt; ]; ]); > +frame{PHP \+ JIT コンパイル}< +fig-center(vconcat [ gap 30pt; mid-textbox{PHP スクリプト} |> with-frame; gap 30pt; hconcat [ mid-textbox{opcode} |> with-frame; gap 20pt; textbox{VM で実行} |> hvmargin 18pt; ]; gap 30pt; hconcat [ mid-textbox{機械語} |> with-frame; gap 20pt; textbox{CPU で実行} |> hvmargin 18pt; ]; ]); > +frame{JIT コンパイルの難点}< +listing{ * コンパイルに時間を割けない ** コンパイルに1分かかるので実行は待ってというわけにはいかない ** コンパイルすればするほど速くなるわけではない * すべての情報が事前に確定しない ** 動的言語。変数が未定義かも、型が想定と違うかも ** \code(`$a`); + \code(`$b`); が常に \code(`int`); と \code(`int`); の足し算とは限らない } > +frame{Tracing JIT}< +p{ コンパイルする前に実際に実行してみて、 何度も繰り返し実行される部分・何度も通る分岐などを特定し、 そこだけピンポイントでコンパイルする } +p{} +p{ INI で \code(`opcache.jit=tracing`); にするとこのモードになる (デフォルトの挙動) } > +frame{Tracing JIT}< +listing{ * コンパイルに時間を割けない ** 繰り返し実行される箇所だけコンパイルする ** これまで何度も実行されたのだから、これからも何度も実行されるはず ** 関数 \code(`f()`); が 1000 回、関数 \code(`g()`); が 1回実行されたなら、\code(`f()`); だけコンパイルする * すべての情報が事前に確定しない ** 実際の実行パターンを元に、そのケースに最適化させた機械語を生成する ** \code(`$a`); + \code(`$b`); が \code(`int`); と \code(`int`); だったときに限定した機械語を生成し、 想定と違っていれば従来の opcode を VM で動かす ** \code(`int`); と \code(`int`); の組み合わせで 1000回、\code(`null`); と \code(`null`); の組み合わせで 1回実行されたなら、\code(`int`); と \code(`int`); に合わせてコンパイルする } > +frame{PHP 8.4 での変更}< +p{} +p{ PHP 8.4 では、JIT コンパイルの仕組みに大きな変更が入る } > +frame{PHP 8.4 での変更}< +p{} +p{ PHP 8.4 では、JIT コンパイルの仕組みに大きな変更が入る } +p{ IR (Intermediate Representation、中間表現) の導入 } > +frame{PHP 8.3 まで}< +fig-center(vconcat [ gap 20pt; mid-textbox{PHP スクリプト} |> with-frame; gap 20pt; hconcat [ mid-textbox{opcode} |> with-frame; gap 20pt; textbox{VM で実行} |> hvmargin 18pt; ]; gap 20pt; hconcat [ mid-textbox{機械語} |> with-frame; gap 20pt; textbox{CPU で実行} |> hvmargin 18pt; ]; ]); > +frame{PHP 8.4 での変更}< +fig-center(vconcat [ gap 20pt; mid-textbox{PHP スクリプト} |> with-frame; gap 20pt; hconcat [ mid-textbox{opcode} |> with-frame; gap 20pt; textbox{VM で実行} |> hvmargin 18pt; ]; gap 20pt; mid-textbox{IR (中間表現)} |> with-frame; gap 20pt; hconcat [ mid-textbox{機械語} |> with-frame; gap 20pt; textbox{CPU で実行} |> hvmargin 18pt; ]; ]); > +frame{現在の実装の問題点}< +listing{ * opcode から直接機械語に変換では最適化しづらい ** opcode の表現に制約を受ける ** 抽象度が足りない ** ソースコードをパースせずに実行するようなもの * CPU アーキテクチャごとに別々の実装を抱える ** 2 アーキテクチャで計 2.7 万行 * PHP と密結合 ** JIT と PHP 両方の知識が必要 ** 誰もメンテナンスできない } > +frame{IR の特徴}< +listing{ * より強力な最適化 ** 最適化しやすいデータ構造に ** SSA \+ CFG から Sea-of-Nodes へ * PHP に依存しない } > +frame{実際どうなった?}< +listing{ * 性能 ** 生成された機械語の速度: 0-5 \% 向上 ** Tracing JIT のコンパイルにかかる時間: ほぼ同等 * メンテナンス性 ** \code(`ext/opcache/jit`); の変更量 *** 5.9万行追加、4.9万行削除 ** アーキテクチャごとのコード生成部 *** x86: 1.1万行追加、1.6万行削除 *** arm64: 0.65万行追加、1.1万行削除 } > +frame{知った気になるまとめ}< > +frame{知った気になるまとめ}< +fig-center(vconcat [ gap 60pt; big-textbox{8.4 で変わる JIT}; ]); > +frame{知った気になるまとめ}< +fig-center(vconcat [ gap 60pt; big-textbox{8.4 で変わる JIT}; ]); +fig-center(vconcat [ gap 40pt; mid-textbox{opcode から直接機械語を生成するのではなく、}; gap 10pt; mid-textbox{中間表現 (IR) を挟むようになった}; ]); > +frame{参考資料1}< +listing{ * \link?:({PHP RFC: JIT})(`https://wiki.php.net/rfc/jit`); * \link?:({PHP RFC: A new JIT implementation based on IR Framework})(`https://wiki.php.net/rfc/jit-ir`); * \link?:({GitHub php/php-src: JIT for PHP based on DynAsm})(`https://github.com/php/php-src/pull/3792`); * \link?:({GitHub php/php-src: Added JIT compiler for x86 and x86_64})(`https://github.com/php/php-src/commit/9a06876072b9ccb023d4a14426ccb587f10882f3`); * \link?:({GitHub dstogov/ir})(`https://github.com/dstogov/ir`); * \link?:({Slides by the author of PHP JIT and its IR})(`https://www.researchgate.net/publication/374470404_IR_JIT_Framework_a_base_for_the_next_generation_JIT_for_PHP`); * \link?:({Nikic blog: PHP-7-Virtual-machine})(`https://www.npopov.com/2017/04/14/PHP-7-Virtual-machine.html`); * \link?:({Nikic blog: How-opcache-works})(`https://www.npopov.com/2021/10/13/How-opcache-works.html`); * \link?:({Nikic blog: The-opcache-optimizer})(`https://www.npopov.com/2022/05/22/The-opcache-optimizer.html`); * \link?:({PHP Internals: Implementing a Range Operator into PHP})(`https://phpinternals.net/articles/implementing_a_range_operator_into_php#updating-the-compilation-stage`); } > +frame{参考資料2}< +listing{ * \link?:({Fedor Indutny's Blog: Sea of Nodes})(`https://darksi.de/d.sea-of-nodes/`); * \link?:({GitHub SeaOfNodes/Simple})(`https://github.com/SeaOfNodes/Simple`); * \link?:({LuaJIT DynAsm})(`https://luajit.org/dynasm.html`); * \link?:({The Unofficial DynASM Documentation})(`https://corsix.github.io/dynasm-doc/`); } > >