フレームグラフ
フレームグラフは何に役立つのか?
フレームグラフは、関数で消費されたCPU時間を可視化する方法です。同期操作に時間をかけすぎている箇所を特定するのに役立ちます。
フレームグラフの作成方法
Node.jsのフレームグラフの作成は難しいと聞いたことがあるかもしれませんが、それは(もう)真実ではありません。Solaris VMはフレームグラフに必要なくなりました!
フレームグラフはperf
の出力から生成されます。これはNode固有のツールではありません。CPU時間消費を可視化する最も強力な方法ですが、Node.js 8以降でJavaScriptコードが最適化される方法に問題がある可能性があります。以下のperf出力の問題セクションを参照してください。
事前パッケージ化されたツールを使用する
ローカルでフレームグラフを生成する単一のステップが必要な場合は、0xを試してください。
本番環境のデプロイメントを診断するには、次のノートをお読みください:0x production servers。
システムのperfツールでフレームグラフを作成する
このガイドの目的は、フレームグラフを作成する手順を示し、各ステップを制御できるようにすることです。
各ステップをよりよく理解したい場合は、詳細について説明する以下のセクションをご覧ください。
それでは、作業に取り掛かりましょう。
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 perf < perfs.out > flamegraph.htm
を実行します。
これで、お気に入りのブラウザでフレームグラフファイルを開き、燃える様子を見てください。色分けされているので、最も彩度の高いオレンジ色のバーに最初に焦点を当てることができます。それらはCPU負荷の高い関数を表している可能性があります。
言及する価値があること - フレームグラフの要素をクリックすると、その周囲の拡大図がグラフの上に表示されます。
実行中のプロセスをサンプリングするために perf
を使用する
これは、中断したくない既に実行中のプロセスからフレームグラフデータを記録するのに最適です。再現が難しい問題を抱えた本番プロセスを想像してみてください。
perf record -F99 -p `pgrep -n node` -- sleep 3
sleep 3
は何のためにあるのでしょうか?これは perf を実行し続けるためにあります。-p
オプションが別の pid を指していても、コマンドはプロセスで実行され、そのプロセスで終了する必要があります。perf は、実際にそのコマンドをプロファイリングしているかどうかに関係なく、渡したコマンドのライフサイクルで実行されます。sleep 3
は、perf が 3 秒間実行されるようにします。
-F
(プロファイリング頻度)が 99 に設定されているのはなぜですか?これは妥当なデフォルトです。必要に応じて調整できます。-F99
は、perf に 1 秒あたり 99 サンプルを取得するように指示します。精度を高めるには、値を大きくします。値を小さくすると、出力が少なくなり、結果の精度が低下します。必要な精度は、CPU を集中的に使用する関数の実際の実行時間によって異なります。目に見える速度低下の原因を探している場合、1 秒あたり 99 フレームで十分すぎるはずです。
3 秒間の perf レコードを取得したら、上記の手順の最後の 2 つでフレームグラフを生成します。
Node.js の内部関数をフィルタリングする
通常、自分の呼び出しのパフォーマンスだけを見たいので、Node.js と V8 の内部関数をフィルタリングすると、グラフがはるかに読みやすくなります。perf ファイルは次のようにクリーンアップできます。
sed -i -r \
-e '/(_libc_start|LazyCompile) |v8::internal::BuiltIn|Stub|LoadIC:\\[\\[' \
-e '/^$/d' \
perf.data > perf.out
フレームグラフを読んで、キーとなる関数がほとんどの時間を占めているのに何かが欠けているように見える場合は、フィルターなしでフレームグラフを生成してみてください。Node.js 自体の問題のまれなケースかもしれません。
Node.js のプロファイリングオプション
--perf-basic-prof-only-functions
と --perf-basic-prof
は、JavaScript コードをデバッグするのに役立つ 2 つのオプションです。他のオプションは Node.js 自体をプロファイリングするために使用されます。これは、このガイドの範囲外です。
--perf-basic-prof-only-functions
は、出力が少ないため、オーバーヘッドが最も少ないオプションです。
なぜそれらが必要なのか?
これらのオプションがなければ、フレームグラフは生成されますが、ほとんどのバーには v8::Function::Call
と表示されます。
Perf
の出力に関する問題
Node.js 8.x V8 パイプラインの変更
Node.js 8.x 以降には、V8 エンジンの JavaScript コンパイルパイプラインに新しい最適化が組み込まれており、perf では関数名/参照に到達できない場合があります。(これは Turbofan と呼ばれます)
その結果、フレームグラフで関数名が正しく取得できない可能性があります。
関数名が表示されるはずの場所に 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 では、--interpreted-frames-native-stack
フラグを使用して Turbofan の問題に対処しています。
node --interpreted-frames-native-stack --perf-basic-prof-only-functions
を実行すると、V8 がどのパイプラインを使用して JavaScript をコンパイルしたかに関係なく、フレームグラフに関数名が表示されます。
フレームグラフ内の壊れたラベル
次のようなラベルが表示される場合
node`_ZN2v88internal11interpreter17BytecodeGenerator15VisitStatementsEPMS0_8Zone
これは、使用している Linux perf がデマングルサポート付きでコンパイルされていないことを意味します。たとえば、https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1396654 を参照してください。
例
フレームグラフ演習で、フレームグラフの取得を練習しましょう!