Upgrade to Pro — share decks privately, control downloads, hide ads and more …

プロファイル結果の可視化三本勝負 in PHP

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for uzulla uzulla
November 06, 2020

プロファイル結果の可視化三本勝負 in PHP

speaker uzulla
in "Laravel 勉強会 in 札幌 vol.3"
https://larasap.connpass.com/event/193566/

ref:
https://github.com/uzulla/xhprof-flamegraphs

Avatar for uzulla

uzulla

November 06, 2020
Tweet

More Decks by uzulla

Other Decks in Programming

Transcript

  1. https://github.com/longxinH/xhprof 「今回はこのxhprofにするぜ(fbが諦めたので、forkが⾊々ある…)」 $ git clone https://github.com/longxinH/xhprof.git ./xhprof $ cd xhprof/extension/

    $ phpize $ ./configure $ make && sudo make install $ vi /path/to/php.ini # 先頭にでも追加 extension = xhprof.so $ mkdir -p xhprof/tmp $ chmod 777 xhprof/tmp
  2. vi index.php <?php /** * October - The PHP platform

    that gets back to basics. * * @package October * @author Alexey Bobkov, Samuel Georges */
  3. 突然タレを⼊れます <?php ini_set("xhprof.output_dir", __DIR__."/xhprof/tmp"); define("START_AT", microtime(true)); require_once (__DIR__ . '/xhprof/xhprof_lib/utils/xhprof_runs.php');

    xhprof_enable(XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY); register_shutdown_function(function () { $id = preg_replace('|/|u', " /", $_SERVER['REQUEST_URI']); $id = preg_replace('/[^a-zA-Z0-9 /]/u', "-", $id); $id .= "_" . sprintf("%d", (microtime(true) - START_AT)) . "ms_"; $runs = new XHProfRuns_Default(); $runs->save_run(xhprof_disable(), $id); }); /** 〜略
  4. 別シェルで xhprof ビューワーを⽴ち上げる $ php -d xhprof.output_dir=`pwd`/xhprof/tmp \ -S 127.0.0.1:3334

    \ -t xhprof/xhprof_html/ # cli でphp.ini のxhprof の出⼒先dir 指定と # ビルトインサーバーのIPaddr:port 指定と # ビューワーアプリのDocRoot 指定してます $ open http://127.0.0.1:3334
  5. ここまでのまとめ index.php をちょっといじるだけでプロファイラを⼊れられる なにもかもわからんアプリでもらくらく! ビューワーは xhprof/xhprof_html/ で php -S であげるのが便利

    リモートからつなぎたいなら、ポートフォワードすると良い なれると数分でここまで来ます 「使ったことがある」ではなく、「秒でいれられる」ようになりましょう
  6. さて、Call graphを⾒ていきましょう 雑な⾒⽅ 「⾚くてデカい奴がヤバい」 「⻩⾊い奴は気になる」 「⽩い所は⼀旦無視だが、いずれ倒すことになる」 数字の読み⽅ Inc → 「こいつから以下」がどれくらいかかっているか

    Excl → 「こいつ⾃体が」どれくらいかかっているか Incがデカイのをたどり、Exclがデカイのを探す ⽮印の太さ 時間のかかり具合(≒ 処理の進む⽅向)
  7. 雑なPreload⽤コード $ vi preload.php <?php # preload.php こいつをphp.ini に書く ini_set("error_reporting",

    '-1'); ini_set("display_errors",'1'); ini_set("log_errors",'1'); include "php_list.php"; foreach ($list as $file) { opcache_compile_file($file); }
  8. 雑にPreloadするphpファイルのリストをつくる $ echo "<?php $list=[];" > php_list.php $ find `pwd`

    |grep -e "\.php$" | php -R 'echo "\$list[] = \"$argn\";".PHP_EOL;' >> php_list.php <?php $list=[]; # php_list.php # 「『とにかく、`.php` ファイル全部いれてみた』などと供述しており」 $list[] = "/tmp/sample/php_list.php"; $list[] = "/tmp/sample/server.php"; $list[] = "/tmp/sample/bootstrap/autoload.php"; $list[] = "/tmp/sample/bootstrap/app.php"; $list[] = "/tmp/sample/index.php"; // めっちゃ⻑いので省略
  9. 起動速度(?) $ wc -l php_list.php 4834 php_list.php # 雑に追加した結果のファイル数 $

    time php -c phpop.ini -r "echo 1;" ( ハチャメチャなWarning) 1 real 0m0.916s user 0m0.621s sys 0m0.274s $ time php -r "echo 1;" 1 real 0m0.123s user 0m0.057s sys 0m0.057s 0.916 - 0.123 = 0.793(sec) # 絞り込まないPreload は結構時間かかる。とはいえ「実⾏」ではないのでこんなもの
  10. # ⼯夫例 $id = preg_replace('|/|u', " /", $_SERVER['REQUEST_URI']); $id =

    preg_replace('/[^a-zA-Z0-9 /]/u', "-", $id); $id .= "_" . sprintf("%d", ((microtime(true) - START_AT))*1000) . "ms_"; $runs = new XHProfRuns_Default(); $runs->save_run(xhprof_disable(), $id); やり⽅は⾊々あるが、「ファイル名なので」 / とか ? とか & とかがあるとおかしくな る、それを避ける必要はある 例えば、 / は全⾓ / にするとか(他も同様に…) まあビューワーを改造しちゃってもいいかもね ここらへん、⾊々⼯夫すると便利。 ログインIDいれたりとか… が!⼿間かけてもダルいので、あきらめが肝⼼。
  11. $ php -i |grep xdebug xdebug xdebug support => enabled

    ( 省略) $ mkdir xdebug_prof/ zend_extension="xdebug.so" # ini_set では設定できんのよなあ、他の⽅法はあるがぐぐってください xdebug.profiler_enable=1 xdebug.profiler_output_dir=/tmp/sample/xdebug_prof $ php -c ./php.ini -S 127.0.0.1:3333 $ ls xdebug_prof/ cachegrind.out.7613 # この⽣成ファイルを指定する
  12. Flame graph Call Graph(例:xhprof),Call Tree(例:cachegrind)以外にFlame Graphという可視化 ⼀定サイクルでスタックトレースをサンプリングして、可視化する 「どのURLが重い」等ではなく、「どの処理がいつもいる・深い」を探す (スレッド処理系⽂化圏の概念) サンプルがタイマーで取られるので、ボロボロこぼれる

    「複数のリクエストをまたいで」ビジュアライズしないと意味が薄い PHPだと不⼈気(パッとまともに動くものがない気がする) xhprofで⽣成されるデータを加⼯して、⽣成するツールはある xhprof_enable ではなく、 xhprof_sample_enable でログを取る サンプリング間隔は xhprof.sampling_interval で変更可能(初期0.1秒)
  13. 以下、Flame graphの作り⽅ # index.php にいれるサンプルコード # コールグラフ作ったときのそれとコードが違う、共存不可能 ini_set("xhprof.output_dir", __DIR__ .

    "/xhprof/tmp"); ini_set("xhprof.sampling_interval", "100"); // デフォルト100000 require_once(__DIR__ . '/xhprof/xhprof_lib/utils/xhprof_runs.php'); xhprof_sample_enable(); register_shutdown_function(function () { $runs = new XHProfRuns_Default(); $runs->save_run(xhprof_disable(), (string)microtime(true)); }); $ rm xhprof/tmp/* # 過去データは必ず⼀度消す $ ab -n 1000 http://localhost:3333 # … などで、⼀定時間負荷をかけつづける。⼤量にファイルが⽣成される。 # 時間をかければかけるほど、精度が上がる。はず。⼀般的には。 # しかし重い!
  14. # ツールをDL する、FlameGraph ⽣成ツール $ git clone [email protected]:brendangregg/FlameGraph.git # 変換ツール…

    の本家(?)がうごかんので、ちょっといじったものです $ git clone [email protected]:uzulla/xhprof-flamegraphs.git # xhprof 結果dir を指定し、⼤量のxhprof ファイルを処理する $ xhprof-flamegraphs/xhprof-sample-to-flamegraph-stacks xhprof/tmp \ | FlameGraph/flamegraph.pl > flamegraph.svg # ⽣成されたsvg をブラウザで開くと、クリック操作できます $ open flamegraph.svg