フレームのグラフ
フレームグラフは何に役立ちますか?
フレームグラフは、関数で費やされた CPU 時間を視覚化する方法です。同期処理に時間がかかりすぎる場所を特定するのに役立ちます。
フレームグラフの作り方
Node.js のフレームグラフを作成するのは難しいと聞いたことがあるかもしれませんが、(もはや) そうではありません。 Solaris vms はフレームグラフには不要です。
フレームグラフは perf
出力から生成されます。これは Node 固有のツールではありません。これは、費やされた CPU 時間を視覚化する最も強力な方法ですが、Node.js 8 以降で JavaScript コードが最適化される方法に問題がある可能性があります。下記のperf 出力の問題 のセクションを参照してください。
あらかじめパッケージ化されたツールを使う
部分的にフレームグラフを作成する単一のステップが必要な場合は、0x を試してください。
運用環境のデプロイメントを診断するために、0x 運用サーバという注意書きを読みましょう。
システムパフォーマンスツールでフレームグラフを作成する
このガイドの目的は、フレームグラフの作成に関連する手順を示し、各手順を管理し続けることです。
それぞれのステップをよりよく理解したいならば、続くセクションを見てください。
それでは作業に取り掛かりましょう。
-
perf
をインストールします (まだインストールされていなければ、通常 linux-tools-common パッケージ経由で利用可能です) -
perf
を実行してみてください - カーネルモジュールが足りないと表示されるかもしれません、それらもインストールしてください -
perf を有効にして node を実行します (Node.js のバージョンに固有のヒントについては perf の出力の問題 を参照してください)
perf record -e cycles:u -g -- node --perf-basic-prof app.js
-
パッケージが足りないために perf を実行できないと表示されていない限り、警告を無視してください。カーネルモジュールのサンプルにアクセスできないという警告が表示されることがあります
-
perf script > perfs.out
を実行して、すぐに視覚化できるデータファイルを生成します。読みやすいグラフにクリーンアップを適用すると便利です -
stackvis がインストールされていない場合は、
npm i -g stackvis
を実行して stackvis をインストールします -
stackvis perf < perfs.out > flamegraph.htm
を実行します
お気に入りのブラウザでフレームグラフファイルを開いて、それが出力されるのを見てください。色分けされているので、最初に最も彩度の高いオレンジ色のバーに集中できます。それらは CPU を多用する機能を表している可能性があります。
言及する価値のあること - フレームグラフの要素をクリックするならば、その周囲のズームインはグラフの上に表示されるでしょう。
perf
を使って実行中のプロセスをサンプリングする
これは、中断したくない、既に実行中のプロセスからフレームグラフデータを記録するのに最適です。再現が困難な問題を伴う製造プロセスを想像してください。
perf record -F99 -p `pgrep -n node` -g -- sleep 3
ちょっと待ってください、何のための sleep 3
でしょうか? それはperf を実行し続けるためにあります。-p
オプションが異なる pid を指していますが、コマンドはプロセス上で実行され、そこで終了する必要があります。perf は、実際にそのコマンドをプロファイリングしているかどうかにかかわらず、渡したコマンドの存続期間にわたって実行されます。sleep 3
は perf が 3 秒間実行されるようにします。
-F
(プロファイリング頻度) が 99 に設定されているのはなぜですか? これは妥当なデフォルトの値です。必要に応じて調整できます。-F99
はサンプルを毎秒 99 取るように perf に指示します。より正確にするには値を増やします。値が低いと出力が少なくなり、精度が低下します。必要な精度は、CPU に負荷がかかる機能が実際にどれくらいの時間実行されるかによって異なります。顕著な減速の理由を探しているなら、毎秒 99 フレームで十分すぎるはずです。
その 3 秒の perf レコードを取得したら、上に書いてある手順の最後の2つを実施してフレームグラフの生成に進みます。
Node.js の内部関数を除外する
通常、自身の呼び出しのパフォーマンスを見たいだけなので、Node.js と V8 の内部関数を除外することでグラフをもっと読みやすくすることができます。次のようにして perf ファイルをクリーンアップできます。
sed -i -r \
-e "/( __libc_start| LazyCompile | v8::internal::| Builtin:| Stub:| LoadIC:|\[unknown\]| LoadPolymorphicIC:)/d" \
-e 's/ LazyCompile:[*~]?/ /' \
perfs.out
フレームグラフを参照していて、何か時間がかかるキー関数が欠けているかのように不思議に思った場合は、フィルタなしでフレームグラフを生成してみてください。Node.js 自体に問題が発生することはまれです。
Node.js のプロファイリングオプション
--perf-basic-prof-only-functions
と --perf-basic-prof
は JavaScript コードをデバッグするのに便利です。Node.js 自体をプロファイリングするために他のオプションが使用されますが、これはこのガイドの範囲外です。
--perf-basic-prof-only-functions
は出力が少なくなるので、オーバーヘッドが最も少ないオプションです。
どうしてそれらを全く必要としないのですか?
ええ、これらのオプションがなくてもフレームグラフが得られますが、ほとんどのバーには v8::Function::Call
というラベルが付いています。
perf
出力の問題
Node.js 8.x V8 パイプラインの変更
Node.js 8.x 以降には、V8 エンジンの JavaScript コンパイルパイプラインに対する新しい最適化が付属しているため、関数名/参照がperf にアクセスできない場合があります。(それはターボファンと呼ばれています)
その結果、関数名がフレームグラフの中で正しく得られないかもしれません。
あなたは、関数名を期待するところで ByteCodeHandler:
に気付くでしょう。
0x にはいくつかの緩和策が組み込まれています。
詳細:
- https://github.com/nodejs/benchmarking/issues/168
- https://github.com/nodejs/diagnostics/issues/148#issuecomment-369348961
Node.js 10 以降
Node.js 10.x は Turbofan の問題に --interpreted-frames-native-stack
フラグを使って対処します。
JavaScript をコンパイルするためにどの V8 パイプラインが使用されたかにかかわらず、フレームグラフの関数名を得るために node --interpreted-frames-native-stack --perf-basic-prof-only-functions
を実行してください。
フレームグラフのラベルが壊れている
このようなラベルが表示されている場合
node`_ZN2v88internal11interpreter17BytecodeGenerator15VisitStatementsEPNS0_8ZoneListIPNS0_9StatementEEE
それは使っている Linux の perf が demangle サポート付きでコンパイルされていないことを意味します。例は https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1396654 を参照してください。
例
フレームグラフ演習を使用してフレームグラフを自分でキャプチャする練習をしてください!