Skip to content

プロファイラ

LuaJITには非常に低いオーバーヘッドを持つ統計的プロファイラが統合されています。これにより、定期的な間隔で現在実行中のスタックや他のパラメータをサンプリングすることができます。

  • 統合プロファイラは三つのレベルからアクセスできます:
    • -jpコマンドラインオプションによって呼び出されるバンドルされた高レベルプロファイラ。
    • プロファイラを制御するための低レベルLua API。
    • プロファイラを制御するための低レベルC API。

高レベルプロファイラ

バンドルされた高レベルプロファイラは基本的なプロファイリング機能を提供します。簡単なテキストの要約やソースコードの注釈を生成します。-jpコマンドラインオプションまたはLuaコードからjit.pモジュールをロードすることでアクセスできます。

要するに、関数名によるCPU使用率のプロファイルを取得するにはこれを実行します:

sh
luajit -jp myapp.lua

バンドルされたプロファイラの明確な目標は、あらゆる可能なオプションを追加したり、特別なプロファイリングニーズに対応したりすることではありません。低レベルプロファイラAPIは以下で説明されています。これらはサードパーティの著者によって高度な機能(例えば、IDEの統合やグラフィカルプロファイラ)を実装するために使用されるかもしれません。

注意

サンプリングは、解釈されたコードとJITコンパイルされたコードの両方で機能します。JITコンパイルされたコードの結果は時々驚くべきものになることがあります。LuaJITはLuaコードを大幅に最適化してインライン化します — ソースコードの行とサンプル化されたマシンコードとの間に単純な一対一の対応はありません。

-jp=[options[,output]]

-jpコマンドラインオプションは高レベルプロファイラを起動します。コマンドラインで実行されたアプリケーションが終了すると、プロファイラは停止し、結果をstdoutまたは指定された出力ファイルに書き出します。

プロファイリングの方法を指定するためのオプション引数について:

  • f — スタックダンプ: 関数名、それ以外の場合はモジュール:行。これがデフォルトモードです。
  • F — スタックダンプ: 同上ですが、モジュール:名前をダンプします。
  • l — スタックダンプ: モジュール:行。
  • <number> — スタックダンプの深さ(呼び出し先 ← 呼び出し元)。デフォルト: 1。
  • -<number> — 逆のスタックダンプの深さ(呼び出し元 → 呼び出し先)。
  • s — 最初のスタックレベルの後でスタックダンプを分割します。深さ ≥ 2 または深さ ≤ -2 を意味します。
  • p — モジュール名の完全なパスを表示します。
  • v — VMの状態を表示します。
  • z — ゾーンを表示します。
  • r — 生のサンプル数を表示します。デフォルト: パーセンテージを表示します。
  • a — ソースコードファイルからの抜粋に注釈を付けます。
  • A — 完全なソースコードファイルに注釈を付けます。
  • G — グラフィカルツール用の生の出力を生成します。
  • m<number> — 表示される最小サンプルパーセンテージ。デフォルト: 3%。
  • i<number> — サンプリング間隔(ミリ秒単位)。デフォルト: 10ms。

注意

実際のサンプリング精度はOSに依存します。

-jp のデフォルト出力は、アプリケーションで最もCPUを消費する場所のリストです。スタックダンプの深さを(例えば)-jp=2 として増やすと、ホットスポットの主な呼び出し元や呼び出し先を示すのに役立ちます。しかし、サンプルの集約はユニークなスタックダンプごとにフラットです。

呼び出し元/呼び出し先の二階層ビュー(分割ビュー)を得るには、-jp=s または -jp=-s を使用します。二階層目に表示されるパーセンテージは、一階層目に対する相対値です。

関数に対して各行でどれだけの時間が費やされているかを見るには、-jp=fl を使用します。

異なるVMの状態やゾーンでどれだけの時間が費やされているかを見るには、-jp=v または -jp=z を使用します。

v/z を f/F/l と組み合わせると、二階層ビューが生成されます。例えば -jp=vf や -jp=fv です。これは、VMの状態やゾーンとホットスポットで費やされる時間を示します。これは「どの時間消費関数が解釈されるだけか」や「特定の関数のガベージコレクタのオーバーヘッドは何か」といった質問に答えるために使用できます。

複数のオプションを組み合わせることができますが、すべての組み合わせが意味をなすわけではありません。上記を参照してください。例えば、-jp=3si4m1 は3つのスタックレベルを4msの間隔でサンプリングし、CPUを消費する関数とその呼び出し元の分割ビューを1%の閾値で表示します。

-jp=a または -jp=A によって生成されるソースコードの注釈は、常にフラットであり、行レベルで表示されます。明らかに、ソースコードファイルはプロファイラスクリプトによって読み取り可能である必要があります。

高レベルプロファイラは、Luaコードからも開始および停止することができます:

lua
require("jit.p").start(オプション, 出力)
...
require("jit.p").stop()

jit.zone — ゾーン

ゾーンは、アプリケーションの異なる部分についての情報を高レベルプロファイラに提供するために使用できます。例えば、ゲームは「AI」ゾーンや「PHYS」ゾーンなどを利用できます。ゾーンは階層的で、スタックとして組織されています。

jit.zone モジュールは明示的にロードする必要があります:

lua
local zone = require("jit.zone")
  • zone("名前") は名前付きゾーンをゾーンスタックにプッシュします。
  • zone() は現在のゾーンをゾーンスタックからポップし、その名前を返します。
  • zone:get() は現在のゾーン名またはnilを返します。
  • zone:flush() はゾーンスタックをフラッシュします。

各ゾーンで費やされた時間を表示するには -jp=z を使用します。ホットスポットに対して相対的に費やされた時間を表示するには、例えば -jp=zf や -jp=fz を使用します。

低レベルLua API

jit.profile モジュールは、Luaコードからプロファイラの低レベルAPIにアクセスするために使用されます。このモジュールは明示的にロードする必要があります:

lua
local profile = require("jit.profile")

このモジュールは、独自の高レベルプロファイラを実装するために使用できます。典型的なプロファイリング実行は、プロファイラを開始し、プロファイラコールバックでスタックダンプをキャプチャし、それらをハッシュテーブルに追加してサンプル数を集約し、プロファイラを停止し、すべてのキャプチャされたスタックダンプを分析することです。プロファイラコールバック内で他のパラメータをサンプリングすることも可能です。しかし、統計を歪める可能性があるため、コールバック内で過度の時間を費やさないことが重要です。

profile.start(mode, cb) — プロファイラの開始

この関数はプロファイラを開始します。mode 引数はオプションを保持する文字列です:

  • f — 関数レベルまでの精度でプロファイリングします。
  • l — 行レベルまでの精度でプロファイリングします。
  • i<number> — サンプリング間隔(ミリ秒単位、デフォルトは10ms)。

注意

実際のサンプリング精度はOSに依存します。

cb 引数は3つの引数を持つコールバック関数です: (thread, samples, vmstate)。コールバックは別のコルーチン上で呼び出され、thread 引数はプロファイリングのためのスタックサンプルを保持する状態です。注意: その状態のスタックを変更したり、その上で関数を呼び出したりしないでください。

samples は、前回のコールバックからの累積サンプル数を示します(通常は1)。

vmstate はプロファイリングタイマーがトリガーされた時点のVM状態を保持します。これは、プロファイリングコールバックが呼び出された時のVMの状態と一致するかもしれませんし、しないかもしれません。状態は 'N' ネイティブ(コンパイル済み)コード、'I' 解釈されたコード、'C' Cコード、'G' ガーベージコレクタ、または 'J' JITコンパイラのいずれかです。

profile.stop() — プロファイラの停止

この関数はプロファイラを停止します。

dump = profile.dumpstack([thread,] fmt, depth) — スタックのダンプ

この関数は効率的にスタックダンプを取得することを可能にします。それはスレッド(コルーチン)のスタックダンプを文字列で返し、fmt 引数に従ってフォーマットされます:

  • p — モジュール名の完全なパスを保持します。それ以外の場合は、ファイル名のみが使用されます。
  • f — それが導き出される場合は関数名をダンプします。そうでなければ、モジュール:行を使用します。
  • F — 同上ですが、モジュール:名前をダンプします。
  • l — モジュール:行をダンプします。
  • Z — 最後にダンプされたフレームの次の文字を消去します。
  • その他の文字は出力文字列にそのまま追加されます。

depth 引数は、スレッドの最上位フレームから始まるダンプするフレームの数を指定します。負の数を指定すると、フレームが逆順にダンプされます。

最初の例では、現在のモジュール名と最大10フレームの行番号を個別の行でリスト表示します。二番目の例では、逆順のすべてのフレーム(最大100)の関数名をセミコロン区切りで表示します:

lua
print(profile.dumpstack(thread, "l\n", 10))
print(profile.dumpstack(thread, "lZ;", -100))

低レベルC API

プロファイラはCコードから直接制御できます。例えば、IDEによる使用のためです。宣言は "luajit.h" にあります(Lua/C API拡張を参照)。

luaJIT_profile_start(L, mode, cb, data) — プロファイラの開始

この関数はプロファイラを開始します。mode 引数の説明については上記を参照してください。

cb 引数は以下の宣言を持つコールバック関数です:

c
typedef void (*luaJIT_profile_callback)(void *data, lua_State *L, int samples, int vmstate);

data はコールバックによって使用されます。L はプロファイリングのためのスタックを持つ状態です。注意: このスタックを変更したり、このスタック上で関数を呼び出したりしないでください — この目的のためには別のコルーチンを使用してください。samples と vmstate の説明については上記を参照してください。

luaJIT_profile_stop(L) — プロファイラの停止

この関数はプロファイラを停止します。

p = luaJIT_profile_dumpstack(L, fmt, depth, len) — スタックのダンプ

この関数は効率的にスタックダンプを取得することを可能にします。fmt と depth の説明については上記を参照してください。

この関数はプロファイラのプライベート文字列バッファを指す const char * を返します。int *len 引数は出力文字列の長さを返します。バッファは次の呼び出しで上書きされ、プロファイラが停止すると解放されます。内容をすぐに使用するか、後で使用するためにコピーする必要があります。