Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 1/78 PHPの関数実行とその計測 PHPの関数実行とその計測 五十嵐 進士 / @sji_ch
Slide 2
Slide 2 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 2/78 私は誰ですか 私は誰ですか サーバサイドプログラマー 株式会社インフィニットループ たぶん月刊 PHP ニュース php-master-changes
Slide 3
Slide 3 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 3/78 仙台民、学生時代は佐世保民 PHP カンファレンス仙台とかやりました PHP 歴は累計 6 年くらい 元は趣味で C 言語を少し
Slide 4
Slide 4 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 4/78
Slide 5
Slide 5 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 5/78 Agenda Agenda PHP の関数実行の仕組みについて PHP の関数実行の計測について
Slide 6
Slide 6 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 6/78 PHP の関数とは PHP の関数とは
Slide 7
Slide 7 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 7/78 プログラムに行わせる一連の処理のまとまり
Slide 8
Slide 8 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 8/78
Slide 9
Slide 9 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 9/78 PHP の関数実行の仕組み PHP の関数実行の仕組み
Slide 10
Slide 10 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 10/78 ZendEngine の概要 ZendEngine の概要 ZendEngine って知ってる? ZendEngine が何のことか大体分かる?
Slide 11
Slide 11 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 11/78 PHP 公式処理系の中心部(エンジン) PHP 4 の頃に Zeev さんと Andi さんが作った 2 字ずつとって Zend
Slide 12
Slide 12 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 12/78 ZendEngine = バイトコードコンパイラ + バイトコードを実行する VM
Slide 13
Slide 13 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 13/78 PHP コードは ZendVM のオペコードにコンパイルされる ZendVM(Executor) がオペコードを実行する
Slide 14
Slide 14 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 14/78
Slide 15
Slide 15 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 15/78
Slide 16
Slide 16 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 16/78 // 実際にはもう少し複雑 foreach ($op_array as $opline) { // 例: [ADD, [1, 2]] [$opcode, $operands] = $opline; $handler = $opcode_handlers[$opcode]; $handler($operands); }
Slide 17
Slide 17 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 17/78 コンパイルの流れ コンパイルの流れ
Slide 18
Slide 18 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 18/78
Slide 19
Slide 19 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 19/78 字句解析 字句解析 ↓
Slide 20
Slide 20 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 20/78 構文解析 構文解析
Slide 21
Slide 21 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 21/78 構文解析(構文規則一部) 構文解析(構文規則一部) top_statement: function_declaration_statement function_declaration_statement: function returns_ref T_STRING '(' parameter_list ')' return_type '{' inner_statement_list '}' returns_ref: /* empty */ | '&' parameter_list: non_empty_parameter_list | /* empty */ non_empty_parameter_list: parameter | non_empty_parameter_list ',' parameter
Slide 22
Slide 22 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 22/78 構文解析(構文規則一部) 構文解析(構文規則一部) parameter: optional_type is_reference is_variadic T_VARIABLE | optional_type is_reference is_variadic T_VARIABLE '=' expr optional_type: /* empty */ | type_expr type_expr: type | '?' type type: T_ARRAY | T_CALLABLE | name
Slide 23
Slide 23 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 23/78 構文解析(構文規則一部) 構文解析(構文規則一部) name: namespace_name | T_NAMESPACE T_NS_SEPARATOR namespace_name | T_NS_SEPARATOR namespace_name namespace_name: T_STRING | namespace_name T_NS_SEPARATOR T_STRING return_type: /* empty */ | ':' type_expr ;
Slide 24
Slide 24 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 24/78 構文解析(構文木) 構文解析(構文木) ↑(先の字句解析結果)を構文規則に当てはめると
Slide 25
Slide 25 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 25/78 構文解析(具象構文木) 構文解析(具象構文木)
Slide 26
Slide 26 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 26/78 構文解析(具象構文木) 構文解析(具象構文木) top_statement_list top_statement function_declaration_statement statement function return_type T_FUNCTION : T_STRING parameter_list ( ) nom_empty_parameter_list parameter parameter , optional_type T_VARIABLE type_expr type name namespace_name T_STRING optional_type T_VARIABLE type_expr type name namespace_name T_STRING type name namespace_name type_expr T_STRING inner_statement_list この先省略 この先省略 top_statement
Slide 27
Slide 27 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 27/78 構文解析(AST) 構文解析(AST) AST_STMT_LIST AST_FUNC_DECL AST_ECHO AST_PARAM_LIST AST_STMT_LIST AST_TYPE AST_PARAM AST_PARAM AST_TYPE a AST_TYPE b AST_RETURN AST_BINARY_OP AST_VAR AST_VAR a b AST_CALL AST_NAME AST_ARG_LIST 1 2
Slide 28
Slide 28 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 28/78 コード生成 コード生成
Slide 29
Slide 29 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 29/78 AST_STMT_LIST AST_FUNC_DECL AST_PARAM_LIST AST_STMT_LIST AST_TYPE AST_PARAM AST_PARAM AST_TYPE a AST_TYPE b AST_RETURN AST_BINARY_OP AST_VAR AST_VAR a b
Slide 30
Slide 30 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 30/78 AST をオペコードにコンパイル AST をオペコードにコンパイル
Slide 31
Slide 31 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 31/78 生成されるコード 生成されるコード $_main: INIT_FCALL 2 128 string("sum") SEND_VAL int(1) 1 SEND_VAL int(2) 2 V0 = DO_UCALL ECHO V0 RETURN int(1) sum: CV0($a) = RECV 1 CV1($b) = RECV 2 T2 = ADD CV0($a) CV1($b) VERIFY_RETURN_TYPE T2 RETURN T2 VERIFY_RETURN_TYPE RETURN null
Slide 32
Slide 32 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 32/78 関数 と op_array 関数 と op_array
Slide 33
Slide 33 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 33/78
Slide 34
Slide 34 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 34/78
Slide 35
Slide 35 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 35/78 関数呼び出しのオペコード 関数呼び出しのオペコード $_main: INIT_FCALL 2 128 string("sum") SEND_VAL int(1) 1 SEND_VAL int(2) 2 V0 = DO_UCALL ECHO V0 RETURN int(1) sum: CV0($a) = RECV 1 CV1($b) = RECV 2 T2 = ADD CV0($a) CV1($b) VERIFY_RETURN_TYPE T2 RETURN T2 VERIFY_RETURN_TYPE RETURN null
Slide 36
Slide 36 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 36/78 VMのスタック VMのスタック
Slide 37
Slide 37 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 37/78 ファイルの読み込み ファイルの読み込み
Slide 38
Slide 38 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 38/78 ここ見ると大抵わかる ここ見ると大抵わかる
Slide 39
Slide 39 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 39/78 処理系の標準処理のフック 処理系の標準処理のフック
Slide 40
Slide 40 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 40/78 Zend Engine の一部内部処理は関数ポインタ zend_compile zend_execute zend_ast_process zend_set_user_opcode_handler 拡張等から色々差し替え可能
Slide 41
Slide 41 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 41/78 ZendEngineでの関数実行まとめ ZendEngineでの関数実行まとめ PHP コードは関数の集まり、一見ないように見えても 関数はオペコードの集まり PHP コードはオペコードにコンパイルされる VM はメモリ上にその時々の状態を持つ ZendEngine の挙動は拡張等でカスタマイズできる
Slide 42
Slide 42 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 42/78 関数実行の計測 関数実行の計測
Slide 43
Slide 43 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 43/78 プログラムの高速化 とりあえず何をするか
Slide 44
Slide 44 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 44/78 推測するな、計測せよ
Slide 45
Slide 45 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 45/78 ボトルネック ボトルネック 実行時間の中の大きい割合を占める部分 経験則として、わりと一部コードが原因で全体の足を引っ張りがち ボトルネックを集中的に改善すると効率が良い
Slide 46
Slide 46 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 46/78 原始的計測 原始的計測
Slide 47
Slide 47 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 47/78 原始的方法の良いところ 原始的方法の良いところ 追加のライブラリやツールのインストールがいらない 任意の区間を計測対象に出来る DB/キャッシュアクセスの共通コードに仕込む等で大枠はつかめる
Slide 48
Slide 48 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 48/78 原始的方法の欠点 原始的方法の欠点 計測箇所ごとにコード修正が必要 細かく見ようとするとダルい 分かりやすくI/Oがボトルネックになっているケース以外で困る
Slide 49
Slide 49 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 49/78 Pro ler とは Pro ler とは 性能解析のためのツール プログラムの各処理の実行性能を収集 統計情報を出力
Slide 50
Slide 50 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 50/78 2種類の計測方式 2種類の計測方式
Slide 51
Slide 51 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 51/78 関数呼び出しフック方式(以降フック方式) サンプリング方式
Slide 52
Slide 52 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 52/78 フック方式 フック方式
Slide 53
Slide 53 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 53/78 フック方式の仕組み フック方式の仕組み op_array を実行する zend_execute_ex() は実際には関数ポインタ 拡張から以下のような処理へ差し替える 開始時刻を取得 差し替え前の zend_execute_ex() を呼び出す 終了時刻を取得 「呼び出し元関数名 + 関数名」をキーにした連想配列へ全呼 び出しを記録
Slide 54
Slide 54 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 54/78 フック方式の例 フック方式の例 xdebug xhprof tideways black re spx
Slide 55
Slide 55 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 55/78 xhprof xhprof Facebook 製の拡張 HHVM 移行で PHP7 未対応のまま捨てられた 関数フック方式 全関数呼び出しをフック black re や tideways 等に派生 xdebug のより軽いのがウリ
Slide 56
Slide 56 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 56/78
Slide 57
Slide 57 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 57/78
Slide 58
Slide 58 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 58/78 tideways tideways 元は xhprof からの派生プロファイラ 区間計測機能を持つ 現在は xhprof ベースではなく、0 からリライトされたクローズドソー スの拡張に xhprof 互換機能のみ切り出した tideways_xhprof が OSS に xhprof 互換の無料で使えるプロファイラの中でもよくメンテされて る
Slide 59
Slide 59 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 59/78
Slide 60
Slide 60 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 60/78
Slide 61
Slide 61 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 61/78 フック型の計測オーバーヘッド フック型の計測オーバーヘッド 計測関数の呼び出しによるオーバヘッド VM のネスト呼び出しによるオーバヘッド これらがほぼ固定時間でかかる チリツモで効く
Slide 62
Slide 62 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 62/78
Slide 63
Slide 63 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 63/78 PHP8 の JIT PHP8 の JIT 来年末に PHP8 リリース JIT が入る I/O バウンドな処理にはあまり関係ないが、PHP コード自体は速く なる VM の実行方式が変わって zend_execute_ex() フックが効かなくな るかも AST 操作等で自動で計測処理を仕込む手もあるが、オーバーヘッ ド問題大きくなる
Slide 64
Slide 64 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 64/78 フック方式まとめ フック方式まとめ あらゆる関数呼び出しを計測可能 計測する箇所ごとに計測コードを差し込む必要がない 計測対象の大量呼び出しに弱い 分かりやすいボトルネックがある場合に便利
Slide 65
Slide 65 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 65/78 サンプリング方式 サンプリング方式
Slide 66
Slide 66 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 66/78 サンプリング方式の仕組み サンプリング方式の仕組み 計測対象と平行動作するプログラムが VM の状態をサンプリング して監視 ExecutorGlobals や VM の呼び出しスタック、実行中の命令位置 など サンプリングしたタイミングでよく実行されている箇所が重い箇所
Slide 67
Slide 67 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 67/78
Slide 68
Slide 68 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 68/78 サンプリング方式の例 サンプリング方式の例 nikic/sample_prof adsr/phpspy krakjoe/trace
Slide 69
Slide 69 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 69/78 nikic/sample_prof nikic/sample_prof C 言語拡張 ソースコードの行レベルで計測がとれる 各関数に散財する変数代入がチリツモで遅い、とかにも気付ける 極度にCPU バウンドな処理で型検査のコストが重い可能性とかに も気付ける
Slide 70
Slide 70 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 70/78 内部はプロファイラの開始時に pthread_create() でスレッドを起動 指定したサンプリングレートで計測スレッドが VM の状態を監視
Slide 71
Slide 71 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 71/78 // 起動自体は他プロファイラと似たような方法でいける sample_prof_start(50); // ここでスレッド生成 register_shutdown_function(function(){ sample_prof_end(); $data = sample_prof_get_data(); file_put_contents(uniqid() . 'prof.txt', serialize($data)); });
Slide 72
Slide 72 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 72/78 adsr/phpspy adsr/phpspy スタンドアロンの C 言語プログラム 拡張ではない
Slide 73
Slide 73 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 73/78
Slide 74
Slide 74 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 74/78 PHP 処理系のメモリレイアウトについての情報を自前で持つ 別プロセスからメモリを読んで VM の内部状態を監視 性能計測もできれば変数も覗き見れる 暴走無限ループ時にコードのどこを実行中かも見れる
Slide 75
Slide 75 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 75/78
Slide 76
Slide 76 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 76/78 Ruby の rbspy が元ネタ
Slide 77
Slide 77 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 77/78 サンプリング方式まとめ サンプリング方式まとめ 計測対象の処理に影響を与えず計測できる 関数呼び出し関係の情報はある程度犠牲になる 大量に呼び出される関数があってもオーバヘッドの累積が無い
Slide 78
Slide 78 text
2019/6/29 reveal.js localhost:8000/?print-pdf/#/ 78/78 おしまい おしまい