Download free for 30 days
Sign in
Upload
Language (EN)
Support
Business
Mobile
Social Media
Marketing
Technology
Art & Photos
Career
Design
Education
Presentations & Public Speaking
Government & Nonprofit
Healthcare
Internet
Law
Leadership & Management
Automotive
Engineering
Software
Recruiting & HR
Retail
Sales
Services
Science
Small Business & Entrepreneurship
Food
Environment
Economy & Finance
Data & Analytics
Investor Relations
Sports
Spiritual
News & Politics
Travel
Self Improvement
Real Estate
Entertainment & Humor
Health & Medicine
Devices & Hardware
Lifestyle
Change Language
Language
English
Español
Português
Français
Deutsche
Cancel
Save
EN
Uploaded by
y-uti
10,298 views
JIT のコードを読んでみた
第 7 回 闇 PHP 勉強会での発表資料です。PHP 8 への搭載を目指して開発が進められている JIT のソースコードを読んでみて、どのような実装になっているかを紹介したものです。
Technology
◦
Read more
9
Save
Share
Embed
Embed presentation
Download
Download to read offline
1
/ 52
2
/ 52
3
/ 52
4
/ 52
5
/ 52
6
/ 52
7
/ 52
8
/ 52
9
/ 52
Most read
10
/ 52
11
/ 52
12
/ 52
13
/ 52
Most read
14
/ 52
15
/ 52
16
/ 52
17
/ 52
18
/ 52
19
/ 52
20
/ 52
21
/ 52
22
/ 52
23
/ 52
24
/ 52
25
/ 52
26
/ 52
27
/ 52
28
/ 52
29
/ 52
30
/ 52
31
/ 52
32
/ 52
33
/ 52
34
/ 52
35
/ 52
36
/ 52
Most read
37
/ 52
38
/ 52
39
/ 52
40
/ 52
41
/ 52
42
/ 52
43
/ 52
44
/ 52
45
/ 52
46
/ 52
47
/ 52
48
/ 52
49
/ 52
50
/ 52
51
/ 52
52
/ 52
More Related Content
PPTX
PHP AST 徹底解説
by
do_aki
PDF
モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―
by
shinjiigarashi
KEY
やはりお前らのMVCは間違っている
by
Koichi Tanaka
PPTX
PHP と SAPI と ZendEngine3 と
by
do_aki
PDF
Where狙いのキー、order by狙いのキー
by
yoku0825
PPTX
世界一わかりやすいClean Architecture
by
Atsushi Nakamura
PPTX
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...
by
NTT DATA Technology & Innovation
PDF
ドメイン駆動設計 の 実践 Part3 DDD
by
増田 亨
PHP AST 徹底解説
by
do_aki
モダン PHP テクニック 12 選 ―PsalmとPHP 8.1で今はこんなこともできる!―
by
shinjiigarashi
やはりお前らのMVCは間違っている
by
Koichi Tanaka
PHP と SAPI と ZendEngine3 と
by
do_aki
Where狙いのキー、order by狙いのキー
by
yoku0825
世界一わかりやすいClean Architecture
by
Atsushi Nakamura
9/14にリリースされたばかりの新LTS版Java 17、ここ3年間のJavaの変化を知ろう!(Open Source Conference 2021 O...
by
NTT DATA Technology & Innovation
ドメイン駆動設計 の 実践 Part3 DDD
by
増田 亨
What's hot
PDF
ソーシャルゲームのためのデータベース設計
by
Yoshinori Matsunobu
PDF
ドメイン駆動設計 失敗したことと成功したこと
by
BIGLOBE Inc.
PDF
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
by
shinjiigarashi
ODP
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
by
pospome
PDF
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
by
Takuto Wada
PPTX
関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門
by
Tadahiro Ishisaka
PDF
例外設計における大罪
by
Takuto Wada
PDF
GoによるWebアプリ開発のキホン
by
Akihiko Horiuchi
PPTX
Rootlessコンテナ
by
Akihiro Suda
PPTX
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~
by
Miki Shimogai
PDF
良い?悪い?コードコメントの書き方
by
Shigenori Sagawa
PDF
C#×LLVM=アセンブラ!? 〜詳説・Burstコンパイラー〜
by
UnityTechnologiesJapan002
PDF
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
by
Koichiro Matsuoka
PDF
イミュータブルデータモデルの極意
by
Yoshitaka Kawashima
PPTX
php-src の歩き方
by
do_aki
PPT
メタプログラミングって何だろう
by
Kota Mizushima
PDF
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
by
Yoshifumi Kawai
PDF
Kotlinアンチパターン
by
Recruit Lifestyle Co., Ltd.
PDF
Go入門
by
Takuya Ueda
PPTX
さくっと理解するSpring bootの仕組み
by
Takeshi Ogawa
ソーシャルゲームのためのデータベース設計
by
Yoshinori Matsunobu
ドメイン駆動設計 失敗したことと成功したこと
by
BIGLOBE Inc.
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
by
shinjiigarashi
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
by
pospome
SQLアンチパターン 幻の第26章「とりあえず削除フラグ」
by
Takuto Wada
関数型・オブジェクト指向宗教戦争に疲れたなたに送るGo言語入門
by
Tadahiro Ishisaka
例外設計における大罪
by
Takuto Wada
GoによるWebアプリ開発のキホン
by
Akihiko Horiuchi
Rootlessコンテナ
by
Akihiro Suda
PostgreSQLクエリ実行の基礎知識 ~Explainを読み解こう~
by
Miki Shimogai
良い?悪い?コードコメントの書き方
by
Shigenori Sagawa
C#×LLVM=アセンブラ!? 〜詳説・Burstコンパイラー〜
by
UnityTechnologiesJapan002
DDD x CQRS 更新系と参照系で異なるORMを併用して上手くいった話
by
Koichiro Matsuoka
イミュータブルデータモデルの極意
by
Yoshitaka Kawashima
php-src の歩き方
by
do_aki
メタプログラミングって何だろう
by
Kota Mizushima
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
by
Yoshifumi Kawai
Kotlinアンチパターン
by
Recruit Lifestyle Co., Ltd.
Go入門
by
Takuya Ueda
さくっと理解するSpring bootの仕組み
by
Takeshi Ogawa
Similar to JIT のコードを読んでみた
PDF
JIT for PHP を試した
by
y-uti
PDF
OPcache の最適化器の今
by
y-uti
PDF
OpenJDK HotSpot C1Compiler Overview
by
nothingcosmos
PDF
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
by
Yoshio Hanawa
PDF
PHP 8 で Web 以外の世界の扉を叩く
by
shinjiigarashi
PDF
PHPの今とこれから2020
by
Rui Hirokawa
JIT for PHP を試した
by
y-uti
OPcache の最適化器の今
by
y-uti
OpenJDK HotSpot C1Compiler Overview
by
nothingcosmos
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
by
Yoshio Hanawa
PHP 8 で Web 以外の世界の扉を叩く
by
shinjiigarashi
PHPの今とこれから2020
by
Rui Hirokawa
More from y-uti
PDF
潜在ディリクレ配分法
by
y-uti
PDF
Active Object
by
y-uti
PDF
目で見る過学習と正則化
by
y-uti
PDF
ロジスティック回帰入門
by
y-uti
PDF
論文紹介 Identifying Implementation Bugs in Machine Learning based Image Classifi...
by
y-uti
PDF
PECL operator で演算子オーバーロード
by
y-uti
PDF
PHP-ML で手書き数字認識
by
y-uti
PDF
スパース推定
by
y-uti
PDF
Kaggle の Titanic チュートリアルに挑戦した話
by
y-uti
PDF
PHP カンファレンス福岡 2017 参加報告
by
y-uti
PDF
分類問題 - 機械学習ライブラリ scikit-learn の活用
by
y-uti
PDF
Task Spooler を試した
by
y-uti
PDF
anyenv + phpenv + php-build が便利すぎる件
by
y-uti
PDF
PHP カンファレンス福岡 参加報告
by
y-uti
PDF
RFC: "var" Deprecation
by
y-uti
PDF
最近の PHP の話
by
y-uti
PDF
Windows で拡張モジュールをビルドしてみた
by
y-uti
PDF
PECL を数えてみた
by
y-uti
PDF
Windows で PHP をビルドしてみた
by
y-uti
PDF
逐次ベイズ学習 - サンプリング近似法の場合 -
by
y-uti
潜在ディリクレ配分法
by
y-uti
Active Object
by
y-uti
目で見る過学習と正則化
by
y-uti
ロジスティック回帰入門
by
y-uti
論文紹介 Identifying Implementation Bugs in Machine Learning based Image Classifi...
by
y-uti
PECL operator で演算子オーバーロード
by
y-uti
PHP-ML で手書き数字認識
by
y-uti
スパース推定
by
y-uti
Kaggle の Titanic チュートリアルに挑戦した話
by
y-uti
PHP カンファレンス福岡 2017 参加報告
by
y-uti
分類問題 - 機械学習ライブラリ scikit-learn の活用
by
y-uti
Task Spooler を試した
by
y-uti
anyenv + phpenv + php-build が便利すぎる件
by
y-uti
PHP カンファレンス福岡 参加報告
by
y-uti
RFC: "var" Deprecation
by
y-uti
最近の PHP の話
by
y-uti
Windows で拡張モジュールをビルドしてみた
by
y-uti
PECL を数えてみた
by
y-uti
Windows で PHP をビルドしてみた
by
y-uti
逐次ベイズ学習 - サンプリング近似法の場合 -
by
y-uti
JIT のコードを読んでみた
1.
JIT のコードを読んでみた 内山 雄司
(@y__uti) 2016-12-11 第7回闇PHP勉強会
2.
自己紹介 内山 雄司 (@y__uti) ◦
http://y-uti.hatenablog.jp/ (phpusers-ja) 仕事 ◦ 受託開発の会社 (株式会社ピコラボ) でプログラマをしています 興味 ◦ プログラミング言語処理系 ◦ 機械学習 2016-12-11 第七回 闇PHP勉強会 2
3.
JIT for PHP
project 2016-12-11 第七回 闇PHP勉強会 3 http://news.php.net/php.internals/95531
4.
(第 107 回
PHP 勉強会での発表資料より) ベンチマーク結果 2016-12-11 第七回 闇PHP勉強会 4 0.00 0.05 0.10 0.15 0.20 0.25 0.30 0.35 0.40 実行時間(秒) 5.6.27 7.0.12 7.1.0RC4 JIT (0edf1e9) Intel Core i5-3337U 1.80GHz 2GB Memory CentOS 7 (VM on Windows7) 各 10 回の実行の平均
5.
JIT による速度向上 2016-12-11 第七回
闇PHP勉強会 5 http://news.php.net/php.internals/96613 開発者 (Dmitry Stogov 氏) による 2016-10-26 の投稿 ◦ bench.php では 3 倍の速度向上 ◦ "real-life apps" では大きな差はない
6.
本日の発表内容 どのように実装されているかソースコードを追ってみました 1. プログラム実行時に JIT
コンパイルを行う仕掛け ◦ OPcache を拡張して opcode handler を差し替え ◦ 関数の実行ごとに JIT 起動条件を確認する 2. JIT コンパイルの処理内容 ◦ CFG, SSA, call-graph 等の情報に基づき DynASM を利用してコード生成 ◦ 生成されたコードを実行するように opcode handler を再度差し替え この一枚で理解できてしまったガチ勢はマサカリの準備を! 2016-12-11 第七回 闇PHP勉強会 6
7.
JIT コンパイルの仕掛け 2016-12-11 第七回
闇PHP勉強会 7
8.
PHP の処理の流れ 以下のコマンドを実行すると何が起きるのか 2016-12-11 第七回
闇PHP勉強会 8 $ php foo.php 処理の流れ 1. sapi/cli/php_cli.c の main 関数から処理が始まり 2. Zend/zend.c の zend_execute_scripts 関数が呼ばれ 3. zend_compile_file 関数でコンパイルして 4. zend_execute 関数で実行する
9.
zend_execute_scripts 関数 in Zend/zend.c PHP
処理系によるプログラム実行の「かなめ」 ◦ ファイルをバイトコード命令列にコンパイルする 2016-12-11 第七回 闇PHP勉強会 9 op_array = zend_compile_file(file_handle, type); zend_execute(op_array, retval); ◦ バイトコード命令列を実行する
10.
op_array 構造体 in Zend/zend_compile.h バイトコード命令列
+ 各種情報 ◦ 関数ごとに一つ ◦ トップレベルに書かれたコード用に一つ 2016-12-11 第七回 闇PHP勉強会 10 struct _zend_op { const void *handler; znode_op op1; znode_op op2; znode_op result; uint32_t extended_value; uint32_t lineno; zend_uchar opcode; zend_uchar op1_type; zend_uchar op2_type; zend_uchar result_type; }; struct _zend_op_array { zend_uchar type; zend_uchar arg_flags[3]; ... uint32_t last; zend_op *opcodes; ... }; 一対多 last が命令数を表す
11.
execute_ex 関数 in Zend/zend_vm_execute.h 各バイトコード命令
(zend_op 構造体) の handler を実行 2016-12-11 第七回 闇PHP勉強会 11 ... while (1) { ... if (UNEXPECTED( (ret = ((opcode_handler_t)OPLINE->handler)()) != 0)) { ... return; } ... } ... 読みやすさのため、一部のマクロを展開して掲載しています
12.
JIT コンパイル付きの実行 op_array (関数)
ごとに以下の処理を行う 2016-12-11 第七回 闇PHP勉強会 12 コンパイルする? 元のコードを実行 コード生成 生成された コードを実行 開始 終了 No Yes
13.
JIT コンパイルの準備 (通常の) コンパイル時に
handler を書き換える 2016-12-11 第七回 闇PHP勉強会 13 コンパイルする? 元のコードを実行 コード生成 生成された コードを実行 開始 終了 No Yes handler handler
14.
OPcache の処理 起動時に zend_compile_file
関数を置き換える in ext/opcache/ZendAccelerator.c 2016-12-11 第七回 闇PHP勉強会 14 static int accel_startup(zend_extension *extension) { ... /* Override compiler */ accelerator_orig_compile_file = zend_compile_file; zend_compile_file = persistent_compile_file; ... } persistent_compile_file 関数がやること ◦ コンパイルしたデータ構造をキャッシュ (本来の仕事) ◦ OPcache 独自の最適化 (おまけ?) ◦ JIT コンパイルのための handler 書き換え (JIT を有効にしたときのみ)[New!]
15.
persistent_compile_file 関数 in ext/opcache/ZendAccelerator.c 2016-12-11
第七回 闇PHP勉強会 15 /* zend_compile() replacement */ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type) { ... // キャッシュ済みでなければ if (!persistent_script) { // コンパイルして persistent_script = opcache_compile_file(...); if (persistent_script) { // キャッシュする persistent_script = cache_script_in_shared_memory(...); } ...
16.
JIT までの道のり in ext/opcache/ZendAccelerator.c 2016-12-11
第七回 闇PHP勉強会 16 static zend_persistent_script *cache_script_in_shared_memory( ... ) in ext/opcache/zend_persist.c zend_persistent_script *zend_accel_script_persist( ... ) static void zend_persist_op_array_ex( ... ) in ext/opcache/zend_persist.c in ext/opcache/jit/zend_jit.c ZEND_API int zend_jit_op_array( zend_op_array *op_array, zend_script *script) ◦ この関数で handler を差し替えている
17.
JIT コンパイルのトリガー in ext/opcache/jit/zend_jit.h いくつかの選択肢が提供されている ◦
ZEND_JIT_ON_SCRIPT_LOAD スクリプトのロード時 ◦ ZEND_JIT_ON_FIRST_EXEC 最初の実行 ◦ ZEND_JIT_ON_PROF_REQUEST 実行頻度 (割合) の高い関数 ◦ ZEND_JIT_ON_HOT_COUNTERS 実行回数が閾値を超えたとき ◦ ZEND_JIT_ON_DOC_COMMENT DocComment の指定に従う 2016-12-11 第七回 闇PHP勉強会 17
18.
ZEND_JIT_ON_FIRST_EXEC in ext/opcache/jit/zend_jit.c 2016-12-11 第七回
闇PHP勉強会 18 opline->handler = (const void*)zend_runtime_jit; zend_runtime_jit 関数は無条件に JIT コンパイルを開始 in ext/opcache/jit/zend_jit.c static void ZEND_FASTCALL zend_runtime_jit(void) { ... opline->handler = ZEND_FUNC_INFO(op_array); // 本来の handler を復元 ... /* perform real JIT for this function */ zend_real_jit_func(op_array, NULL, NULL); // JIT コンパイルを実行 ... }
19.
ZEND_JIT_ON_PROF_REQUEST in ext/opcache/jit/zend_jit.c 2016-12-11 第七回
闇PHP勉強会 19 opline->handler = zend_jit_profile_helper; zend_jit_profile_helper は op_array ごとの実行回数を数える in ext/opcache/jit/zend_jit_vm_helpers.c void ZEND_FASTCALL zend_jit_profile_helper(void) { zend_op_array *op_array = (zend_op_array*)EX(func); const void *handler = (const void*)ZEND_FUNC_INFO(op_array); // 本来の handler を取得 ++(ZEND_COUNTER_INFO(op_array)); // この op_array の実行回数 ++zend_jit_profile_counter; // 全 op_array の実行回数の総和 return ((zend_vm_opcode_handler_t)handler)(); // 本来の処理を実行 }
20.
ZEND_JIT_ON_PROF_REQUEST in ext/opcache/jit/zend_jit.c OPcache が
deactivate されるときに JIT コンパイルを行う 2016-12-11 第七回 闇PHP勉強会 20 void zend_jit_check_funcs(HashTable *function_table, zend_bool is_method) { ... zend_ulong counter = (zend_ulong)ZEND_COUNTER_INFO(op_array); if (((double)counter / (double)zend_jit_profile_counter) > ZEND_JIT_PROF_THRESHOLD) { zend_real_jit_func(op_array, NULL, NULL); } ... } ◦ 実行回数の比率が閾値を超えた op_array を JIT の対象とする ◦ 最初のリクエストの実行で判断
21.
ZEND_JIT_ON_HOT_COUNTERS in ext/opcache/jit/zend_jit.c 2016-12-11 第七回
闇PHP勉強会 21 return zend_jit_setup_hot_counters(op_array); 基本ブロック単位で実行回数を計測する in ext/opcache/jit/zend_jit.c static int zend_jit_setup_hot_counters(zend_op_array *op_array) { ... // Control Flow Graph を構築 opline->handler = (const void*)zend_jit_func_counter_helper; for (i = 0; i < cfg.blocks_count; i++) { ... op_array->opcodes[cfg.blocks[i].start].handler = (const void*)zend_jit_loop_counter_helper; } }
22.
ZEND_JIT_ON_HOT_COUNTERS in ext/opcache/jit/zend_jit_vm_helpers.c 実行のたびに hot
counter を減じて 0 以下になったらコンパイル 2016-12-11 第七回 闇PHP勉強会 22 void ZEND_FASTCALL zend_jit_func_counter_helper(void) { ... zend_jit_hot_counters[n] -= ZEND_JIT_HOT_FUNC_COST; if (UNEXPECTED(zend_jit_hot_counters[n] <= 0)) { zend_jit_hot_counters[n] = ZEND_JIT_HOT_COUNTER_INIT; zend_jit_hot_func(execute_data, opline); // handler を復元して JIT 実行 } else { zend_vm_opcode_handler_t *handlers = (zend_vm_opcode_handler_t*)ZEND_FUNC_INFO(&EX(func)->op_array); handlers[opline - EX(func)->op_array.opcodes](); } } ◦ zend_jit_loop_counter_helper も同様の実装
23.
ZEND_JIT_ON_SCRIPT_LOAD in ext/opcache/jit/zend_jit.c 2016-12-11 第七回
闇PHP勉強会 23 return zend_real_jit_func(op_array, script, NULL); OPcache が op_array を処理するタイミングでコンパイルする その意味で、これは "Just In Time" ではない
24.
ZEND_JIT_ON_DOC_COMMENT in ext/opcache/jit/zend_jit.c 2016-12-11 第七回
闇PHP勉強会 24 if (zend_needs_manual_jit(op_array)) { return zend_real_jit_func(op_array, script, NULL); } else { return SUCCESS; } OPcache が op_array を処理するタイミングでコンパイルする DocComment に @jit を指定した関数のみ対象 これも "Just In Time" ではない
25.
ここまでのまとめ JIT のソースコードを追ってみました 1. プログラム実行時に
JIT コンパイルを行う仕掛け ◦ OPcache を拡張して opcode handler を差し替え ◦ 関数の実行ごとに JIT 起動条件を確認する 2016-12-11 第七回 闇PHP勉強会 25
26.
JIT コンパイルの処理内容 2016-12-11 第七回
闇PHP勉強会 26
27.
zend_real_jit_func in ext/opcache/jit/zend_jit.c 2016-12-11 第七回
闇PHP勉強会 27 static int zend_real_jit_func( zend_op_array *op_array, zend_script *script, const zend_op *rt_opline) { ... JIT コンパイルのための解析 (OPcache の実装を利用) ◦ 制御フローグラフの構築 ◦ データフロー解析 (ZEND_JIT_LEVEL_OPT_FUNC 以上) ◦ コールグラフの構築 (ZEND_JIT_LEVEL_OPT_FUNCS 以上) コード生成 ◦ opcode ごとに機械語を生成 ◦ コード生成の仕組みには DynASM を利用
28.
コード生成の概要 [1/2] in ext/opcache/jit/zend_jit.c JIT
コンパイルの本体 2016-12-11 第七回 闇PHP勉強会 28 static int zend_jit( zend_op_array *op_array, zend_ssa *ssa, const zend_op *rt_opline) { ... // op_array に含まれる各 opcode をコンパイルする switch (opline->opcode) { ... case ZEND_PRE_INC: case ZEND_PRE_DEC: case ZEND_POST_INC: case ZEND_POST_DEC: if (!zend_jit_inc_dec(&dasm_state, opline, op_array, ssa)) { goto jit_failure; } goto done; }
29.
コード生成の実装 DynASM のフォーマットで記述 in ext/opcache/jit/zend_jit_x86.dasc 2016-12-11
第七回 闇PHP勉強会 29 static int zend_jit_inc_dec( dasm_State **Dst, const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa) { ... || if ((opline->opcode == ZEND_POST_INC || opline->opcode == ZEND_POST_DEC) && || opline->result_type != IS_UNUSED) { | ZVAL_COPY_VALUE FP + opline->result.var, FP + opline->op1.var, MAY_BE_LONG, r0, eax, r1 || } if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) { | inc aword [FP + opline->op1.var] } else { | dec aword [FP + opline->op1.var] } ... ◦ dynasm.lua によって zend_jit_x86.c に変換される
30.
DynASM is a Dynamic
Assembler for code generation engines. 2016-12-11 第七回 闇PHP勉強会 30 https://luajit.org/dynasm.html
31.
生成されるコード DynASM のフォーマットでの記述から in ext/opcache/jit/zend_jit_x86.dasc 2016-12-11
第七回 闇PHP勉強会 31 if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) { | inc aword [FP + opline->op1.var] } else { | dec aword [FP + opline->op1.var] } if (opline->opcode == ZEND_PRE_INC || opline->opcode == ZEND_POST_INC) { //| inc aword [FP + opline->op1.var] dasm_put(Dst, 749, opline->op1.var); } else { //| dec aword [FP + opline->op1.var] dasm_put(Dst, 755, opline->op1.var); } 以下のような C のコードが生成される in ext/opcache/jit/zend_jit_x86.c
32.
大雑把な理解 アセンブリのテンプレートエンジン 2016-12-11 第七回 闇PHP勉強会
32 | inc aword [FP + opline->op1.var] dasm_put(Dst, 749, opline->op1.var); static const unsigned char dasm_actions[5499] = { 248,10,198,4,37,237,0,128,60,37,237,0,15,132, ... "inc aword [FP + ]" ... } zend_jit_x86.dasc zend_jit_x86.c + テンプレートの定義 テンプレートの適用 dynasm.lua
33.
コード生成の概要 [2/2] in ext/opcache/jit/zend_jit.c JIT
コンパイルの本体 2016-12-11 第七回 闇PHP勉強会 33 ... // すべての opcode を処理した後 handler = dasm_link_and_encode( &dasm_state, op_array, &ssa->cfg, rt_opline, NULL); } ◦ 以後は生成された機械語をハンドラとして実行する
34.
JIT コンパイルの例 2016-12-11 第七回
闇PHP勉強会 34
35.
サンプルプログラム 0 から 9
までの和を計算して表示する 2016-12-11 第七回 闇PHP勉強会 35 <?php function f($a) { $sum = 0; for ($i = 0; $i < $a; ++$i) { $sum += $i; } return $sum; } $a = 10; $ans = f($a); echo $ans;
36.
JIT の動作の確認 JIT コンパイルの結果を表示する 2016-12-11
第七回 闇PHP勉強会 36 $ ./sapi/cli/php -d zend_extension=$(pwd)/modules/opcache.so -d opcache.enable_cli=1 -d opcache.jit=15 -d opcache.jit_buffer_size=32M -d opcache.jit_debug=3 sample1.php 2>&1 ◦ opcache.jit ◦ トリガー (10 の位) とレベル (1 の位) をまとめて指定 ◦ opcache.jit_debug ◦ デバッグ情報の表示内容を指定
37.
CFG 構築結果の表示 ZEND_JIT_DEBUG_SSA (=
2) を立てるとフロー解析結果を表示 2016-12-11 第七回 闇PHP勉強会 37 f: ; (lines=9, args=1, vars=3, tmps=1, ssa_vars=0) ; (JIT) ; /home/y-uti/php-jit-bench/sample-code/sample1.php:3-10 BB0: start lines=[0-0] ; to=(BB1) ; level=0 ; children=(BB1) CV0($a) = RECV 1 // 仮引数 $a に 1 番目の実引数を受け取る BB1: follow entry lines=[1-3] ; from=(BB0) ; to=(BB3) ; idom=BB0 ; level=1 ; children=(BB3) CV1($sum) = QM_ASSIGN int(0) // $sum = 0 CV2($i) = QM_ASSIGN int(0) // $i = 0 JMP BB3 // 基本ブロック BB3 にジャンプ BB2: target lines=[4-5] ; from=(BB3) ...
38.
関数 f の
CFG 2016-12-11 第七回 闇PHP勉強会 38 BB0: CV0($a) = RECV 1 // $a = 第 1 引数 BB1: CV1($sum) = QM_ASSIGN int(0) // $sum = 0 CV2($i) = QM_ASSIGN int(0) // $i = 0 JMP BB3 // jump to BB3 BB2: CV1($sum) = ADD CV1($sum) CV2($i) // $sum = $sum + $i PRE_INC CV2($i) // ++$i BB3: T3 = IS_SMALLER CV2($i) CV0($a) // $i < $a ? JMPNZ T3 BB2 // if true jump to BB2 BB4: RETURN CV1($sum) // return $sum
39.
生成コードの確認 ZEND_JIT_DEBUG_ASM (= 1)
を立てると生成されるコードを表示 2016-12-11 第七回 闇PHP勉強会 39 JIT$f: ; (/home/y-uti/php-jit-bench/sample-code/sample1.php) sub $0x8, %rsp call ZEND_RECV_SPEC_HANDLER cmp $0x0, EG(exception) jnz JIT$$exception_handler jmp .L1 .ENTRY1: sub $0x8, %rsp .L1: call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER jmp .L3 .L2: mov $0x7fcd524d6c10, %r15 call ZEND_ADD_SPEC_CV_CV_HANDLER cmp $0x0, EG(exception) jnz JIT$$exception_handler call ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER ...
40.
ZEND_JIT_LEVEL_MINIMAL "Subroutine threading" 2016-12-11 第七回
闇PHP勉強会 40 ... .L1: call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER call ZEND_QM_ASSIGN_NOREF_SPEC_CONST_HANDLER jmp .L3 .L2: mov $0x7fcd524d6c10, %r15 call ZEND_ADD_SPEC_CV_CV_HANDLER cmp $0x0, EG(exception) jnz JIT$$exception_handler call ZEND_PRE_INC_LONG_OR_DOUBLE_SPEC_TMPVARCV_RETVAL_UNUSED_HANDLER cmp $0x0, EG(exception) jnz JIT$$exception_handler .L3: ... ◦ PHP のハンドラ (in Zend/zend_vm_execute.h) 呼び出しを並べる ◦ JMP 系の命令は遷移先を直接指定する形に展開
41.
ZEND_JIT_LEVEL_INLINE "Selective inline threading" 2016-12-11
第七回 闇PHP勉強会 41 ... .L1: mov $0x0, 0x60(%r14) // val($sum) = 0 mov $0x4, 0x68(%r14) // typeinfo($sum) = IS_LONG (= 4) mov $0x0, 0x70(%r14) // val($i) = 0 mov $0x4, 0x78(%r14) // typeinfo($i) = IS_LONG jmp .L3 .L2: mov $0x7fd46c0d6c10, %r15 call ZEND_ADD_SPEC_CV_CV_HANDLER // $sum = $sum + $i (これは展開されない) cmp $0x0, EG(exception) jnz JIT$$exception_handler cmp $0x4, 0x78(%r14) jnz .L6 ... ◦ 命令の処理を展開して高速に実行 ◦ 型推論なし
42.
ZEND_JIT_LEVEL_OPT_FUNC "Optimized JIT based
on Type-Inference" 2016-12-11 第七回 闇PHP勉強会 42 ... .L2: cmp $0x4, 0x68(%r14) // $sum の型が IS_LONG か調べる jnz .L10 // IS_LONG でなければ .L10 へ cmp $0x4, 0x78(%r14) // $i の型が IS_LONG か調べる jnz .L8 // IS_LONG でなければ .L8 へ mov 0x60(%r14), %rax // %rax = $sum add 0x70(%r14), %rax // %rax = %rax + $i jo .L9 // オーバーフローしたら .L9 へ mov %rax, 0x60(%r14) // $sum = %rax .L3: cmp $0x4, 0x78(%r14) // $i の型が IS_LONG か調べる jnz .L13 // IS_LONG でなければ .L13 へ inc 0x70(%r14) // ++$i jo .L12 // オーバーフローしたら .L12 へ ... ◦ 型推論 (SSA 形式のデータフロー解析) に基づく最適化
43.
より積極的な最適化レベル ZEND_JIT_LEVEL_OPT_FUNCS ◦ "Optimized JIT
based on Type-Inference and call-tree" ZEND_JIT_LEVEL_OPT_SCRIPT ◦ "Optimized JIT based on Type-Inference and inner-procedure analises" (サンプルプログラムでは違いが見えなかったので割愛) 2016-12-11 第七回 闇PHP勉強会 43
44.
まとめ JIT のソースコードを追ってみました 1. プログラム実行時に
JIT コンパイルを行う仕掛け ◦ OPcache を拡張して opcode handler を差し替え ◦ 関数の実行ごとに JIT 起動条件を確認する 2. JIT コンパイルの処理内容 ◦ CFG, SSA, call-graph 等の情報に基づき DynASM を利用してコード生成 ◦ 生成されたコードを実行するように opcode handler を再度差し替え 2016-12-11 第七回 闇PHP勉強会 44
45.
補足 このスライドは下記のコードに基づいて作成しました ◦ Repository: https://github.com/zendtech/php-src.git ◦
Branch: jit-dynasm ◦ Commit: 39a5bd9 2016-12-11 第七回 闇PHP勉強会 45
46.
JIT コンパイルの例 (追加) 2016-12-11
第七回 闇PHP勉強会 46
47.
サンプルプログラム 以下の PHP プログラムを考える 2016-12-11
第七回 闇PHP勉強会 47 <?php function f() { $a = 1; ++$a; ++$a; ++$a; return $a; } echo f(); ◦ JIT コンパイラはどのようなコードを生成するか
48.
前提として 最適化コンパイラ (たとえば gcc)
なら直接 4 を返せる 2016-12-11 第七回 闇PHP勉強会 48 // sample2.c int f() { int a = 1; ++a; ++a; ++a; return a; } $ gcc -O -S sample2.c $ cat sample2.s .file "sample2.c" .text .globl f .type f, @function f: .LFB0: .cfi_startproc movl $4, %eax ret .cfi_endproc .LFE0: .size f, .-f .ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-4)" .section .note.GNU-stack,"",@progbits
49.
ZEND_JIT_LEVEL_INLINE 「データフロー解析を行わない」とは? 2016-12-11 第七回 闇PHP勉強会
49 BB0: start exit lines=[0-4] ; level=0 CV0($a) = QM_ASSIGN int(1) PRE_INC CV0($a) PRE_INC CV0($a) PRE_INC CV0($a) RETURN CV0($a) ◦ 最初の $a = 1 は右辺が定数なので整数だと分かる ◦ 後続の ++$a で $a が整数 (IS_LONG) だということは分からない
50.
ZEND_JIT_LEVEL_INLINE 以下のコードが生成される 2016-12-11 第七回 闇PHP勉強会
50 JIT$f: ; (/home/y-uti/php-jit-bench/sample-code/sample2.php) sub $0x8, %rsp mov $0x1, 0x50(%r14) mov $0x4, 0x58(%r14) cmp $0x4, 0x58(%r14) jnz .L5 inc 0x50(%r14) jo .L4 .L1: cmp $0x4, 0x58(%r14) jnz .L11 inc 0x50(%r14) jo .L10 .L2: cmp $0x4, 0x58(%r14) jnz .L17 inc 0x50(%r14) jo .L16 ...
51.
ZEND_JIT_LEVEL_OPT_FUNC データフロー解析を行う 2016-12-11 第七回 闇PHP勉強会
51 BB0: start exit lines=[0-4] ; level=0 #1.CV0($a) [long] RANGE[1..1] = QM_ASSIGN int(1) PRE_INC #1.CV0($a) [long] RANGE[1..1] -> #2.CV0($a) [long] RANGE[2..2] PRE_INC #2.CV0($a) [long] RANGE[2..2] -> #3.CV0($a) [long] RANGE[3..3] PRE_INC #3.CV0($a) [long] RANGE[3..3] -> #4.CV0($a) [long] RANGE[4..4] RETURN #4.CV0($a) [long] RANGE[4..4] ◦ 各命令で $a が取り得る型と値が解析されている
52.
ZEND_JIT_LEVEL_OPT_FUNC 以下のコードが生成される 2016-12-11 第七回 闇PHP勉強会
52 JIT$f: ; (/home/y-uti/php-jit-bench/sample-code/sample2.php) sub $0x8, %rsp mov $0x1, 0x50(%r14) mov $0x4, 0x58(%r14) inc 0x50(%r14) inc 0x50(%r14) inc 0x50(%r14) mov 0x10(%r14), %rcx test %rcx, %rcx jz .L1 mov 0x50(%r14), %rdx mov %rdx, (%rcx) mov $0x4, 0x8(%rcx) .L1: ... ◦ 整数型でありオーバーフローもしないことが分かっている ◦ しかし、あくまでも「バイトコード命令ごとに」コード生成 ◦ gcc のように 4 を返すような最適化はしない
Download