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
Submit search
EN
Uploaded by
y-uti
10,294 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
Most read
29
/ 52
30
/ 52
31
/ 52
32
/ 52
33
/ 52
34
/ 52
35
/ 52
36
/ 52
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-src の歩き方
by
do_aki
PPTX
PHP と SAPI と ZendEngine3 と
by
do_aki
PDF
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
by
NTT DATA Technology & Innovation
PDF
Quick tour of PHP from inside
by
julien pauli
PPTX
イベント駆動プログラミングとI/O多重化
by
Gosuke Miyashita
PDF
組み込み関数(intrinsic)によるSIMD入門
by
Norishige Fukushima
PDF
C/C++プログラマのための開発ツール
by
MITSUNARI Shigeo
PDF
目grep入門 +解説
by
murachue
php-src の歩き方
by
do_aki
PHP と SAPI と ZendEngine3 と
by
do_aki
Javaコードが速く実⾏される秘密 - JITコンパイラ⼊⾨(JJUG CCC 2020 Fall講演資料)
by
NTT DATA Technology & Innovation
Quick tour of PHP from inside
by
julien pauli
イベント駆動プログラミングとI/O多重化
by
Gosuke Miyashita
組み込み関数(intrinsic)によるSIMD入門
by
Norishige Fukushima
C/C++プログラマのための開発ツール
by
MITSUNARI Shigeo
目grep入門 +解説
by
murachue
What's hot
PDF
Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2
by
tamtam180
PPTX
php and sapi and zendengine2 and...
by
do_aki
PDF
ARM CPUにおけるSIMDを用いた高速計算入門
by
Fixstars Corporation
PDF
OpenJDKのコミッタってどんなことしたらなったの?解決してきた技術課題の事例から見えてくる必要な知識と技術(JJUG CCC 2023 Spring)
by
NTT DATA Technology & Innovation
PDF
今日からできる!簡単 .NET 高速化 Tips
by
Takaaki Suzuki
PDF
Unified JVM Logging
by
Yuji Kubota
PPTX
C#で速度を極めるいろは
by
Core Concept Technologies
PPTX
BuildKitによる高速でセキュアなイメージビルド
by
Akihiro Suda
PDF
CentOS Linux 8 の EOL と対応策の検討
by
Masahito Zembutsu
PDF
プログラムの処方箋~健康なコードと病んだコード
by
Shigenori Sagawa
PDF
プログラムを高速化する話
by
京大 マイコンクラブ
PDF
Javaのログ出力: 道具と考え方
by
Taku Miyakawa
PPTX
MVPパターンによる設計アプローチ「あなたのアプリ報連相できてますか」
by
U-dai Yokoyama
PPTX
Effective Modern C++ 勉強会 Item 22
by
Keisuke Fukuda
PDF
ゲーム開発者のための C++11/C++14
by
Ryo Suzuki
PPTX
リアルタイムサーバー 〜Erlang/OTPで作るPubSubサーバー〜
by
Yugo Shimizu
PDF
【Unite 2018 Tokyo】60fpsのその先へ!スマホの物量限界に挑んだSTG「アカとブルー」の開発設計
by
UnityTechnologiesJapan002
PDF
PHP7で変わること ——言語仕様とエンジンの改善ポイント
by
Yoshio Hanawa
PDF
Parser combinatorってなんなのさ
by
cct-inc
KEY
やはりお前らのMVCは間違っている
by
Koichi Tanaka
Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2
by
tamtam180
php and sapi and zendengine2 and...
by
do_aki
ARM CPUにおけるSIMDを用いた高速計算入門
by
Fixstars Corporation
OpenJDKのコミッタってどんなことしたらなったの?解決してきた技術課題の事例から見えてくる必要な知識と技術(JJUG CCC 2023 Spring)
by
NTT DATA Technology & Innovation
今日からできる!簡単 .NET 高速化 Tips
by
Takaaki Suzuki
Unified JVM Logging
by
Yuji Kubota
C#で速度を極めるいろは
by
Core Concept Technologies
BuildKitによる高速でセキュアなイメージビルド
by
Akihiro Suda
CentOS Linux 8 の EOL と対応策の検討
by
Masahito Zembutsu
プログラムの処方箋~健康なコードと病んだコード
by
Shigenori Sagawa
プログラムを高速化する話
by
京大 マイコンクラブ
Javaのログ出力: 道具と考え方
by
Taku Miyakawa
MVPパターンによる設計アプローチ「あなたのアプリ報連相できてますか」
by
U-dai Yokoyama
Effective Modern C++ 勉強会 Item 22
by
Keisuke Fukuda
ゲーム開発者のための C++11/C++14
by
Ryo Suzuki
リアルタイムサーバー 〜Erlang/OTPで作るPubSubサーバー〜
by
Yugo Shimizu
【Unite 2018 Tokyo】60fpsのその先へ!スマホの物量限界に挑んだSTG「アカとブルー」の開発設計
by
UnityTechnologiesJapan002
PHP7で変わること ——言語仕様とエンジンの改善ポイント
by
Yoshio Hanawa
Parser combinatorってなんなのさ
by
cct-inc
やはりお前らのMVCは間違っている
by
Koichi Tanaka
Similar to JIT のコードを読んでみた
PDF
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
by
Yoshio Hanawa
PPTX
PHP AST 徹底解説
by
do_aki
PDF
OPcache の最適化器の今
by
y-uti
PDF
PHP 8 で Web 以外の世界の扉を叩く
by
shinjiigarashi
PDF
PHPの今とこれから2020
by
Rui Hirokawa
PDF
JIT for PHP を試した
by
y-uti
PDF
OpenJDK HotSpot C1Compiler Overview
by
nothingcosmos
OPcacheの新機能ファイルベースキャッシュの内部実装を読んでみた
by
Yoshio Hanawa
PHP AST 徹底解説
by
do_aki
OPcache の最適化器の今
by
y-uti
PHP 8 で Web 以外の世界の扉を叩く
by
shinjiigarashi
PHPの今とこれから2020
by
Rui Hirokawa
JIT for PHP を試した
by
y-uti
OpenJDK HotSpot C1Compiler Overview
by
nothingcosmos
More from y-uti
PDF
潜在ディリクレ配分法
by
y-uti
PDF
PECL operator で演算子オーバーロード
by
y-uti
PDF
Windows で拡張モジュールをビルドしてみた
by
y-uti
PDF
Active Object
by
y-uti
PDF
Task Spooler を試した
by
y-uti
PDF
anyenv + phpenv + php-build が便利すぎる件
by
y-uti
PDF
目で見る過学習と正則化
by
y-uti
PDF
Windows で PHP をビルドしてみた
by
y-uti
PDF
PECL を数えてみた
by
y-uti
PDF
ロジスティック回帰入門
by
y-uti
PDF
分類問題 - 機械学習ライブラリ scikit-learn の活用
by
y-uti
PDF
PHP カンファレンス福岡 参加報告
by
y-uti
PDF
PHP-ML で手書き数字認識
by
y-uti
PDF
逐次ベイズ学習 - サンプリング近似法の場合 -
by
y-uti
PDF
Kaggle の Titanic チュートリアルに挑戦した話
by
y-uti
PDF
RFC: "var" Deprecation
by
y-uti
PDF
最近の PHP の話
by
y-uti
PDF
論文紹介 Identifying Implementation Bugs in Machine Learning based Image Classifi...
by
y-uti
PDF
PHP カンファレンス福岡 2017 参加報告
by
y-uti
PDF
スパース推定
by
y-uti
潜在ディリクレ配分法
by
y-uti
PECL operator で演算子オーバーロード
by
y-uti
Windows で拡張モジュールをビルドしてみた
by
y-uti
Active Object
by
y-uti
Task Spooler を試した
by
y-uti
anyenv + phpenv + php-build が便利すぎる件
by
y-uti
目で見る過学習と正則化
by
y-uti
Windows で PHP をビルドしてみた
by
y-uti
PECL を数えてみた
by
y-uti
ロジスティック回帰入門
by
y-uti
分類問題 - 機械学習ライブラリ scikit-learn の活用
by
y-uti
PHP カンファレンス福岡 参加報告
by
y-uti
PHP-ML で手書き数字認識
by
y-uti
逐次ベイズ学習 - サンプリング近似法の場合 -
by
y-uti
Kaggle の Titanic チュートリアルに挑戦した話
by
y-uti
RFC: "var" Deprecation
by
y-uti
最近の PHP の話
by
y-uti
論文紹介 Identifying Implementation Bugs in Machine Learning based Image Classifi...
by
y-uti
PHP カンファレンス福岡 2017 参加報告
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