プロファイラ
プログラムの開発が終了し、いざ本番環境で動かしてみると思ったより動作が遅いことがあります。しかし、プログラムが複雑になるにつれその原因を見つけるのは困難になります。プログラムの中でどの部分がボトルネックになっているのかを効率よく調べるにはプロファイラというツールを使います。
v8-profilerというモジュールを使うと、node-inspector(注)と併用してプロファイラを使うことができます。v8-profilerをインストールするには、プロジェクトのディレクトリで次のコマンドを実行します。
$ npm install v8-profiler
そして、プログラム中でリスト5の要領でプロファイラにデータを収集させます。
profiler = require 'v8-profiler' fib = (n) -> if n < 2 n else fib(n-1) + fib(n-2) profiler.startProfiling 'prof1' # prof1という名前で記録開始 result = fib 38 console.log result profiler.stopProfiling 'prof1' # 記録終了 debugger # スクリプトが終了しないように一時停止する
このリストを実行した時、profiler.startProfiling()
からprofiler.stopProfiling()
までのCPUプロファイル情報が収集されます。ここではプロファイル名としてprof1を指定していますが、指定する名前を変更することで、1つのプログラム内の複数箇所で同時にプロファイルを行うことができます。
最後のプロファイル箇所の後ろに1つだけdebugger
文を入れておき、ターミナルの別のタブでnode-inspectorを起動します。
$ node-inspector
そして、プロファイル対象のプログラムを--nodejs --debug-brk
というオプションを付けて実行します。
$ coffee --nodejs --debug-brk test.coffee debugger listening on port 5858
あるいは、nodeプログラムを直接起動する場合は--debug-brk
オプションだけを付けます。
$ node --debug-brk test.js debugger listening on port 5858
ブラウザでhttp://0.0.0.0:8080/debug?port=5858を開くと図2のような画面になります。
右上のボタンを押すとプログラムの実行が始まり、しばらくしてdebugger文に到達すると自動的に一時停止して図3の画面になります。
ここで「Profiles」をクリックし、プロファイルを有効にすると図4のようなProfilesパネルが表示されます。
右三角の印をクリックすると階層が現れます。この階層は、その関数がどの関数から呼ばれたのかをボトムアップ式に表しています。図4では、NativeModule.compile
がNativeModule.require
から呼ばれ、NativeModule.require
はcreateWritableStdioStream
から呼ばれたことを示しています。
下のバーにある「%」ボタンをクリックすると、実際にかかった時間での表示に切り替わります(図5)。「s」は秒、「ms」はミリ秒(1000分の1秒)を表します。
「%」ボタンの左隣にある「Heavy (Bottom Up)」ボタンをクリックして「Tree (Top Down)」を選択すると階層が逆になり、トップダウン式の表示に切り替わります(図6)。
「Self」の列に表示されている実行時間は、呼び出し先の関数の実行時間を含まず、その関数自身のコードの実行時間だけを表しています。Self列でのソートは先ほどのボトムアップ式の表示では便利ですが、トップダウン表示の場合は「Total」列をクリックして「呼び出し先の関数の実行時間を含めた時間」でソートすると見やすくなります。
ここではfib
という関数が再帰的に呼ばれ、全体のほとんどの時間がその処理にかかっていることがわかります。ボトルネックがfib
関数であることが確認できたので、fib
関数を重点的に改善すればプログラムの大幅な高速化が見込めそうだということがわかります。
node-inspector(BSDライセンス)をインストールするには、以下のコマンドを実行します。
$ npm install -g node-inspector
まとめ
以上、Node.jsでアプリケーションを作る上で指針となる開発手法をかいつまんで解説しました。次回からはブラウザで動くJavaScript開発のベストプラクティス編をお送りします。お楽しみに!