OPcache の最適化器の今

2,108 views

Published on

PHP Conference 2017 での発表資料です。
PHP 7.1 から OPcache 内部の最適化器にデータフロー解析が実装され、より効率

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,108
On SlideShare
0
From Embeds
0
Number of Embeds
1,328
Actions
Shares
0
Downloads
2
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

OPcache の最適化器の今

  1. 1. OPcache の最適化器の今 内山 雄司 (@y__uti) Japan PHP Conference 2017
  2. 2. 自己紹介 内山 雄司 (@y__uti) ◦ http://y-uti.hatenablog.jp/ (phpusers-ja) 仕事 ◦ 受託開発の会社 (株式会社ピコラボ) でプログラマをしています 興味 ◦ プログラミング言語処理系 ◦ 機械学習 2017-10-08 JAPAN PHP CONFERENCE 2017 2
  3. 3. ベンチマークテスト 0.0 0.5 1.0 1.5 2.0 2.5 3.0 5.5.38 5.6.31 7.0.24 7.1.10 7.2.0RC3 実行時間[秒] PHP バージョン Zend/bench.php の実行時間 (各 5 回の実行の平均) OPcache 無効 OPcache 有効 2017-10-08 JAPAN PHP CONFERENCE 2017 3 Core i5-3337U CentOS 7 (VM 上の Guest OS)
  4. 4. ベンチマークテスト 0.0 0.5 1.0 1.5 2.0 2.5 3.0 5.5.38 5.6.31 7.0.24 7.1.10 7.2.0RC3 実行時間[秒] PHP バージョン Zend/bench.php の実行時間 (各 5 回の実行の平均) OPcache 無効 OPcache 有効 2017-10-08 JAPAN PHP CONFERENCE 2017 4 Core i5-3337U CentOS 7 (VM 上の Guest OS) PHP 7 で高速化
  5. 5. ベンチマークテスト 0.0 0.5 1.0 1.5 2.0 2.5 3.0 5.5.38 5.6.31 7.0.24 7.1.10 7.2.0RC3 実行時間[秒] PHP バージョン Zend/bench.php の実行時間 (各 5 回の実行の平均) OPcache 無効 OPcache 有効 2017-10-08 JAPAN PHP CONFERENCE 2017 5 Core i5-3337U CentOS 7 (VM 上の Guest OS) OPcache 有効時 PHP 7.1 以降も速度向上
  6. 6. 発表の流れ ベンチマーク結果の紹介 PHP のプログラム実行と OPcache OPcache の最適化器の変遷 ◦ PHP 5.5 ~ 7.0 ◦ PHP 7.1 ◦ PHP 7.2 2017-10-08 JAPAN PHP CONFERENCE 2017 6
  7. 7. PHP プログラムの実行方式 バイトコードインタプリタ方式 2017-10-08 JAPAN PHP CONFERENCE 2017 7 PHP ファイルをコンパイルする インタプリタで実行する
  8. 8. プログラム実行の様子 プログラムをバイトコード命令列にコンパイルして逐次実行 2017-10-08 JAPAN PHP CONFERENCE 2017 8 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 ADD ~0 1, 2 1 IS_SMALLER ~1 0, ~0 2 JMPZ ~1, ->8 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  9. 9. プログラム実行の様子 プログラムをバイトコード命令列にコンパイルして逐次実行 2017-10-08 JAPAN PHP CONFERENCE 2017 9 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 ADD ~0 1, 2 1 IS_SMALLER ~1 0, ~0 2 JMPZ ~1, ->8 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  10. 10. プログラム実行の様子 プログラムをバイトコード命令列にコンパイルして逐次実行 2017-10-08 JAPAN PHP CONFERENCE 2017 10 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 ADD ~0 1, 2 1 IS_SMALLER ~1 0, ~0 2 JMPZ ~1, ->8 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  11. 11. プログラム実行の様子 プログラムをバイトコード命令列にコンパイルして逐次実行 2017-10-08 JAPAN PHP CONFERENCE 2017 11 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 ADD ~0 1, 2 1 IS_SMALLER ~1 0, ~0 2 JMPZ ~1, ->8 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  12. 12. プログラム実行の様子 プログラムをバイトコード命令列にコンパイルして逐次実行 2017-10-08 JAPAN PHP CONFERENCE 2017 12 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 ADD ~0 1, 2 1 IS_SMALLER ~1 0, ~0 2 JMPZ ~1, ->8 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  13. 13. プログラム実行の様子 プログラムをバイトコード命令列にコンパイルして逐次実行 2017-10-08 JAPAN PHP CONFERENCE 2017 13 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 ADD ~0 1, 2 1 IS_SMALLER ~1 0, ~0 2 JMPZ ~1, ->8 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  14. 14. プログラム実行の様子 プログラムをバイトコード命令列にコンパイルして逐次実行 2017-10-08 JAPAN PHP CONFERENCE 2017 14 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 ADD ~0 1, 2 1 IS_SMALLER ~1 0, ~0 2 JMPZ ~1, ->8 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  15. 15. プログラム実行の様子 プログラムをバイトコード命令列にコンパイルして逐次実行 2017-10-08 JAPAN PHP CONFERENCE 2017 15 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 ADD ~0 1, 2 1 IS_SMALLER ~1 0, ~0 2 JMPZ ~1, ->8 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  16. 16. OPcache の役割 バイトコード命令をキャッシュして再利用 2017-10-08 JAPAN PHP CONFERENCE 2017 16 バイトコードをキャッシュする PHP ファイルをコンパイルする キャッシュから バイトコードを取得する キャッシュ済み? インタプリタで実行する NO YES
  17. 17. OPcache の役割 バイトコード命令を最適化する 2017-10-08 JAPAN PHP CONFERENCE 2017 17 キャッシュから バイトコードを取得する キャッシュ済み? インタプリタで実行する NO YES バイトコードを最適化する バイトコードをキャッシュする PHP ファイルをコンパイルする
  18. 18. 発表の流れ ベンチマーク結果の紹介 PHP のプログラム実行と OPcache OPcache の最適化器の変遷 ◦ PHP 5.5 ~ 7.0 ◦ PHP 7.1 ◦ PHP 7.2 2017-10-08 JAPAN PHP CONFERENCE 2017 18
  19. 19. PHP 5.5 の最適化器 2017-10-08 JAPAN PHP CONFERENCE 2017 19 PASS 3 連続するジャンプの短絡など PASS 10 NOP 命令の除去 PASS 9 一時変数のメモリ領域共有 PASS 5 制御フローグラフを用いた最適化 PASS 2 条件分岐の置き換えなど PASS 1 定数式の置き換えなど
  20. 20. 素朴な最適化の例 出発点:最適化を行わない場合 2017-10-08 JAPAN PHP CONFERENCE 2017 20 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 ADD ~0 1, 2 1 IS_SMALLER ~1 0, ~0 2 JMPZ ~1, ->8 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  21. 21. PASS 1 定数式の置き換え 1 + 2 > 0 は true だと分かるので・・・ 2017-10-08 JAPAN PHP CONFERENCE 2017 21 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 ADD ~0 1, 2 1 IS_SMALLER ~1 0, ~0 2 JMPZ ~1, ->8 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  22. 22. PASS 1 定数式の置き換え コンパイル時に計算してしまう 2017-10-08 JAPAN PHP CONFERENCE 2017 22 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 NOP 1 NOP 2 JMPZ <bool>, ->8 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  23. 23. PASS 2 条件分岐の置き換え if 文の true/false はコンパイル時に決まっているので・・・ 2017-10-08 JAPAN PHP CONFERENCE 2017 23 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 NOP 1 NOP 2 JMPZ <bool>, ->8 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  24. 24. PASS 2 条件分岐の置き換え NOP (飛ばない場合) か JMP (飛ぶ場合) に置き換える 2017-10-08 JAPAN PHP CONFERENCE 2017 24 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 NOP 1 NOP 2 NOP 3 JMP ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  25. 25. PASS 3 連続ジャンプの短絡 JMP 命令の飛び先も JMP 命令なので・・・ 2017-10-08 JAPAN PHP CONFERENCE 2017 25 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 NOP 1 NOP 2 NOP 3 JMP ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  26. 26. PASS 3 連続ジャンプの短絡 最後の飛び先まで一気にジャンプする 2017-10-08 JAPAN PHP CONFERENCE 2017 26 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 NOP 1 NOP 2 NOP 3 JMP ->6 4 ECHO 'A' 5 JMP ->9 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  27. 27. PASS 10 NOP 命令の除去 NOP (何もしない命令) を実行しても何もしないのだから・・・ 2017-10-08 JAPAN PHP CONFERENCE 2017 27 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 NOP 1 NOP 2 NOP 3 JMP ->6 4 ECHO 'A' 5 JMP ->9 6 ECHO 'B' 7 JMP ->9 8 ECHO 'C' 9 RETURN null  ソースコード  コンパイル結果
  28. 28. PASS 10 NOP 命令の除去 消す 2017-10-08 JAPAN PHP CONFERENCE 2017 28 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 JMP ->3 1 ECHO 'A' 2 JMP ->6 3 ECHO 'B' 4 JMP ->6 5 ECHO 'C' 6 RETURN null  ソースコード  コンパイル結果
  29. 29. PASS 5 制御フローグラフ 処理の流れをグラフ構造で表現して各種最適化を行う 2017-10-08 JAPAN PHP CONFERENCE 2017 29 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } 0 ADD ~0 1, 2 1 IS_SMALLER ~1 0, ~0 2 JMPZ ~1, ->8  ソースコード  制御フローグラフ 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 9 RETURN null 8 ECHO 'C'
  30. 30. PASS 5 制御フローグラフ この例ではどこを通るか決まっているので・・・ 2017-10-08 JAPAN PHP CONFERENCE 2017 30 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } 0 ADD ~0 1, 2 1 IS_SMALLER ~1 0, ~0 2 JMPZ ~1, ->8  ソースコード  制御フローグラフ 3 JMPZ <bool>, ->6 4 ECHO 'A' 5 JMP ->7 6 ECHO 'B' 7 JMP ->9 9 RETURN null 8 ECHO 'C'
  31. 31. PASS 5 制御フローグラフ ここまで最適化される (PHP 5.5 の頃からこの程度はできていた) 2017-10-08 JAPAN PHP CONFERENCE 2017 31 function test() { if (1 + 2 > 0) { if (false) { echo "A"; } else { echo "B"; } } else { echo "C"; } } # op return operands ---------------------------------- 0 ECHO 'B' 1 RETURN null  ソースコード  コンパイル結果
  32. 32. PHP 5.6 の最適化器 2017-10-08 JAPAN PHP CONFERENCE 2017 32 PASS 4 関数呼び出しの効率化 PASS 11 未使用リテラルの除去 PASS 9 PASS 5PASS 1 PASS 2 PASS 3 PASS 10
  33. 33. PHP 7.0 の最適化器 2017-10-08 JAPAN PHP CONFERENCE 2017 33 PASS 9 PASS 5PASS 1 PASS 2 PASS 3 PASS 10 PASS 4 PASS 11 PASS 12 関数スタックフレームの最適化
  34. 34. PHP 7.1 の最適化器 2017-10-08 JAPAN PHP CONFERENCE 2017 34 PASS 9 PASS 1 PASS 2 PASS 3 PASS 10 PASS 11 PASS 5 PASS 6 データフロー解析に基づく最適化 PASS 12 PASS 7 コールグラフの構築 PASS 4 (関数呼び出しの効率化) → PASS 16 関数のインライン化 (極めて限定的)
  35. 35. PASS 7 コールグラフ 関数間の呼び出し関係をグラフ構造で表現して各種最適化を行う 2017-10-08 JAPAN PHP CONFERENCE 2017 35 function test() { return f(); } function f() { return x() + y(); } function x() { ... } function y() { ... }  ソースコード  コールグラフ f test x y ただし現時点では解析結果を積極的には利用していない様子
  36. 36. PASS 6 データフロー解析 プログラムの各点でデータが取り得る型や値を解析する 2017-10-08 JAPAN PHP CONFERENCE 2017 36 function test() { $a = 1 + 2; $b = $a + 3; if ($b > 0) { $a = 4; } else { $b = $a * 5.6; } return $a + $b; }  ソースコード  バイトコード命令列 (データフロー解析前) 0 ASSIGN !0, 3 1 ADD ~2 !0, 3 2 ASSIGN !1, ~2 3 IS_SMALLER ~2 0, !1 4 JMPZ ~2, ->7 5 ASSIGN !0, 4 6 JMP ->9 7 MUL ~2 !0, 5.6 8 ASSIGN !1, ~2 9 ADD ~2, !0, !1 10 RETURN ~2
  37. 37. PASS 6 データフロー解析 プログラムの各点でデータが取り得る型や値を解析する 2017-10-08 JAPAN PHP CONFERENCE 2017 37 function test() { $a = 1 + 2; $b = $a + 3; if ($b > 0) { $a = 4; } else { $b = $a * 5.6; } return $a + $b; }  ソースコード  制御フローグラフ 9 ADD ~2, !0, !1 10 RETURN ~2 0 ASSIGN !0, 3 1 ADD ~2 !0, 3 2 ASSIGN !1, ~2 3 IS_SMALLER ~2 0, !1 4 JMPZ ~2, ->7 5 ASSIGN !0, 4 6 JMP ->9 7 MUL ~2 !0, 5.6 8 ASSIGN !1, ~2
  38. 38. PASS 6 データフロー解析 プログラムの各点でデータが取り得る型や値を解析する 2017-10-08 JAPAN PHP CONFERENCE 2017 38 function test() { $a = 1 + 2; $b = $a + 3; if ($b > 0) { $a = 4; } else { $b = $a * 5.6; } return $a + $b; }  ソースコード  制御フローグラフ return $a + $b; $a = 3; $b = $a + 3; if ($b > 0) $a = 4; $b = $a * 5.6;
  39. 39. PASS 6 データフロー解析 プログラムの各点でデータが取り得る型や値を解析する 2017-10-08 JAPAN PHP CONFERENCE 2017 39 function test() { $a = 1 + 2; $b = $a + 3; if ($b > 0) { $a = 4; } else { $b = $a * 5.6; } return $a + $b; }  ソースコード  制御フローグラフ return $a + $b; $a = 3; $b = $a + 3; if ($b > 0) $a = 4; $b = $a * 5.6;
  40. 40. PASS 6 データフロー解析 プログラムの各点でデータが取り得る型や値を解析する 2017-10-08 JAPAN PHP CONFERENCE 2017 40 function test() { $a = 1 + 2; $b = $a + 3; if ($b > 0) { $a = 4; } else { $b = $a * 5.6; } return $a + $b; }  ソースコード  制御フローグラフ return $a + $b; $a = 3; $b = $a + 3; if ($b > 0) $a = 4; $b = $a * 5.6;
  41. 41. PASS 6 データフロー解析 プログラムの各点でデータが取り得る型や値を解析する 2017-10-08 JAPAN PHP CONFERENCE 2017 41 function test() { $a = 1 + 2; $b = $a + 3; if ($b > 0) { $a = 4; } else { $b = $a * 5.6; } return $a + $b; }  ソースコード  制御フローグラフ $a <- $a or $a; return $a + $b; $a = 3; $b = $a + 3; if ($b > 0) $a = 4; $b = $a * 5.6; 静的単一代入形式 (SSA) と呼ばれる
  42. 42. PASS 6 データフロー解析 プログラムの各点でデータが取り得る型や値を解析する 2017-10-08 JAPAN PHP CONFERENCE 2017 42 function test() { $a = 1 + 2; $b = $a + 3; if ($b > 0) { $a = 4; } else { $b = $a * 5.6; } return $a + $b; }  ソースコード  制御フローグラフ $a <- $a or $a; $b <- $b or $b; return $a + $b; $a = 3; $b = $a + 3; if ($b > 0) $a = 4; $b = $a * 5.6;
  43. 43. PASS 6 データフロー解析 プログラムの各点でデータが取り得る型や値を解析する 2017-10-08 JAPAN PHP CONFERENCE 2017 43 function test() { $a = 1 + 2; $b = $a + 3; if ($b > 0) { $a = 4; } else { $b = $a * 5.6; } return $a + $b; }  ソースコード  制御フローグラフ $a <- $a or $a; // [3, 4]: int $b <- $b or $b; // ?: [int, float] return $a + $b; $a = 3; // 3: int $b = $a + 3; // 6: int if ($b > 0) // 4: int $a = 4; // ?: float $b = $a * 5.6;
  44. 44. 解析結果の用途 型や値に応じた効率的なバイトコード命令への変換 2017-10-08 JAPAN PHP CONFERENCE 2017 44 function test():int { $a = 1; $a = $a + 1; return $a; } # op return operands ---------------------------------- 0 ASSIGN !0, 1 1 ASSIGN_ADD !0, 1 2 VERIFY_RETURN_TYPE !0 3 RETURN !0  ソースコード  コンパイル結果 (データフロー解析なし) 0 QM_ASSIGN !0 1 1 PRE_INC !0 2 RETURN !0  コンパイル結果 (データフロー解析あり) PHP 7.1 時点ではそれほど積極的な実装はされていない様子
  45. 45. 解析結果の用途 型に特化したハンドラの選択 "ADD 命令" は大量にある (データの種類や型によって "適切な ADD" を選択) 2017-10-08 JAPAN PHP CONFERENCE 2017 45 ZEND_ADD_SPEC_CONST_CONST_HANDLER ZEND_ADD_SPEC_CONST_TMPVAR_HANDLER ZEND_ADD_SPEC_CONST_CV_HANDLER ZEND_ADD_SPEC_TMPVAR_CONST_HANDLER ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER ZEND_ADD_SPEC_TMPVAR_CV_HANDLER ZEND_ADD_SPEC_CV_CONST_HANDLER ZEND_ADD_SPEC_CV_TMPVAR_HANDLER ZEND_ADD_SPEC_CV_CV_HANDLER ZEND_ADD_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER ZEND_ADD_LONG_SPEC_CONST_TMPVARCV_HANDLER ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER ZEND_ADD_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER 7.1 で追加
  46. 46. 解析結果の用途 型に特化したハンドラの選択 "ADD 命令" は大量にある (データの種類や型によって "適切な ADD" を選択) 2017-10-08 JAPAN PHP CONFERENCE 2017 46 ZEND_ADD_SPEC_CONST_CONST_HANDLER ZEND_ADD_SPEC_CONST_TMPVAR_HANDLER ZEND_ADD_SPEC_CONST_CV_HANDLER ZEND_ADD_SPEC_TMPVAR_CONST_HANDLER ZEND_ADD_SPEC_TMPVAR_TMPVAR_HANDLER ZEND_ADD_SPEC_TMPVAR_CV_HANDLER ZEND_ADD_SPEC_CV_CONST_HANDLER ZEND_ADD_SPEC_CV_TMPVAR_HANDLER ZEND_ADD_SPEC_CV_CV_HANDLER ZEND_ADD_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER ZEND_ADD_LONG_NO_OVERFLOW_SPEC_TMPVARCV_TMPVARCV_HANDLER ZEND_ADD_LONG_SPEC_CONST_TMPVARCV_HANDLER ZEND_ADD_LONG_SPEC_TMPVARCV_TMPVARCV_HANDLER ZEND_ADD_DOUBLE_SPEC_CONST_TMPVARCV_HANDLER ZEND_ADD_DOUBLE_SPEC_TMPVARCV_TMPVARCV_HANDLER 7.1 で追加 比 較
  47. 47. 特化したハンドラの威力 ハンドラの違いによる実装の比較 2017-10-08 JAPAN PHP CONFERENCE 2017 47 static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_SPEC_CONST_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zval *op1, *op2, *result; op1 = EX_CONSTANT(opline->op1); op2 = _get_zval_ptr_cv_undef(execute_data, opline->op2.var); if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) { if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { result = EX_VAR(opline->result.var); fast_long_add_function(result, op1, op2); ZEND_VM_NEXT_OPCODE(); } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { result = EX_VAR(opline->result.var); ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) + Z_DVAL_P(op2)); ZEND_VM_NEXT_OPCODE(); } } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) { if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) { result = EX_VAR(opline->result.var); ZVAL_DOUBLE(result, Z_DVAL_P(op1) + Z_DVAL_P(op2)); ZEND_VM_NEXT_OPCODE(); } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) { result = EX_VAR(opline->result.var); ZVAL_DOUBLE(result, Z_DVAL_P(op1) + ((double)Z_LVAL_P(op2))); ZEND_VM_NEXT_OPCODE(); } } SAVE_OPLINE(); if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) { op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R); } if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) { op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } add_function(EX_VAR(opline->result.var), op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); }  従来のハンドラ (CONST_CV)  特化ハンドラ (LONG_NO_OVERFLOW) static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ADD_LONG_NO_OVERFLOW_SPEC_CONST_TMPVARCV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zval *op1, *op2, *result; op1 = EX_CONSTANT(opline->op1); op2 = EX_VAR(opline->op2.var); result = EX_VAR(opline->result.var); ZVAL_LONG(result, Z_LVAL_P(op1) + Z_LVAL_P(op2)); ZEND_VM_NEXT_OPCODE(); }
  48. 48. PHP 7.2 の最適化器 2017-10-08 JAPAN PHP CONFERENCE 2017 48 PASS 9 PASS 1 PASS 2 PASS 3 PASS 10 PASS 4 → PASS 16 PASS 11 PASS 5 PASS 13 未使用変数の除去 PASS 12 PASS 6 (データフロー解析) PASS 7 (コールグラフの構築) → PASS 8 定数伝播 → PASS 14 不要コード除去
  49. 49. PASS 8 定数伝播 定数式をコンパイル時に計算する 例:PHP 7.1 までは・・・ 2017-10-08 JAPAN PHP CONFERENCE 2017 49 function test() { $a = 1 + 2; $b = $a + 3; $c = $b + 4; echo $c; } compiled vars: !0=$a, !1=$b, !2=$c op return operands ------------------------------------- QM_ASSIGN !0 3 ADD !1 3, !0 ADD !2 4, !1 ECHO !2 RETURN null  ソースコード  コンパイル結果 $a の右辺だけは計算されるが計算結果が後続の命令に伝わらない
  50. 50. PASS 8 定数伝播 定数式をコンパイル時に計算する 例:定数伝播を適用すると 2017-10-08 JAPAN PHP CONFERENCE 2017 50 function test() { $a = 1 + 2; $b = $a + 3; $c = $b + 4; echo $c; } compiled vars: !0=$a, !1=$b, !2=$c op return operands ------------------------------------- QM_ASSIGN !0 3 QM_ASSIGN !1 6 QM_ASSIGN !2 10 ECHO !2 RETURN null  ソースコード  コンパイル結果 PHP 7.2 で最適化レベルを指定 optimization_level=0x7fff8fff $b や $c の右辺もコンパイル時に計算され定数になる
  51. 51. PASS 14 不要コードの除去 不要なバイトコード命令を除去する 例:不要コード除去も適用すると 2017-10-08 JAPAN PHP CONFERENCE 2017 51 function test() { $a = 1 + 2; $b = $a + 3; $c = $b + 4; echo $c; } compiled vars: !0=$a, !1=$b, !2=$c op return operands ------------------------------------- ECHO 10 RETURN null  ソースコード  コンパイル結果 PHP 7.2 で最適化レベルを指定 optimization_level=0x7fffafff $a, $b, $c に代入する命令が除去され 10 が直接出力される
  52. 52. PASS 13 未使用変数の除去 使われることのない変数を除去する 例:未使用変数の除去も適用すると 2017-10-08 JAPAN PHP CONFERENCE 2017 52 function test() { $a = 1 + 2; $b = $a + 3; $c = $b + 4; echo $c; } compiled vars: none op return operands ------------------------------------- ECHO 10 RETURN null  ソースコード  コンパイル結果 PHP 7.2 のデフォルトの設定 optimization_level=0x7fffbfff スタックに $a, $b, $c の領域を確保しなくなる (PASS 12 の処理)
  53. 53. もう少し複雑な例 2017-10-08 JAPAN PHP CONFERENCE 2017 53 # op return operands ---------------------------------- 0 RETURN 10  ソースコード  コンパイル結果 function test() { $a = 1 + 2; $b = $a + 3; if ($b > 0) { $a = 4; } else { $b = $a * 5.6; } return $a + $b; }
  54. 54. まだ出来ていないこと いろいろある 例:ループを含むコード 2017-10-08 JAPAN PHP CONFERENCE 2017 54 function test() { $a = 1; for ($i=0; $i<3; ++$i) { ++$a; } echo $a; } # op return operands ------------------------------ 1 QM_ASSIGN !0 1 2 QM_ASSIGN !1 0 3 JMP ->5 4 PRE_INC !0 5 PRE_INC !1 6 IS_SMALLER ~2 !1, 3 7 JMPNZ ~2, ->3 8 ECHO !0 9 RETURN null  ソースコード  コンパイル結果
  55. 55. まとめ と 補足 PHP 7.1 以降で OPcache の最適化器が大きく進歩 コマンドラインからの PHP 実行で OPcache を有効にするには 2017-10-08 JAPAN PHP CONFERENCE 2017 55 $ php -d opcache.enable_cli=1 bench.php

×
Save this presentationTap To Close