DTrace は
ただし,
そこで今回は,
アセンブラレベルから見た独自プロバイダのオーバヘッド
独自プロバイダの実現方式
独自プロバイダの実現方式について説明するに当たって,
独自プロバイダのプローブ呼び出しを行うソースをコンパイルした直後,
リスト 1 プローブ呼び出しのアセンブラコード
subl $0x8,%esp ; スタックフレーム拡張
pushl $0xa ; 第2引数(4バイト)の準備
pushl $0x0 ; 第1引数(4バイト)の準備
call __dtrace_checkpoint___pass
addl $0x10,%esp ; 引数領域の解放
このオブジェクトファイルに対して,dtrace -G
" 実行によるリンク前処理を行うことで,
リスト 2 前処理後のアセンブラコード
subl $0x8,%esp ; スタックフレーム拡張
pushl $0xa ; 第2引数(4バイト)の準備
pushl $0x0 ; 第1引数(4バイト)の準備
nop
nop
nop
nop
nop
addl $0x10,%esp ; 引数領域の解放
"call __
"に必要な5バイトの領域が,nop
によって置き換えられているのがわかります。
この前処理によって,nop
によって確保されたままです。つまり,
第3回での pid
プロバイダの実現方式確認の手順を利用すれば,
- ※1)
dis
コマンド等による実際の逆アセンブル出力では,シンボル解決が未実施であるため, " call __
"ではなく,dtrace_ checkpoint___ pass " call -0x4 <main+0x2d>
"といった形式で出力されます。
また,ここでの例における第1引数は文字列領域へのアドレスですが, これもシンボル解決が未実施であるため, 具体的なアドレス値ではなく0x0が設定されています。
プローブ無効化方式に関する考察
先述したように,dtrace -G
"実行によるリンク前処理によって,__
"関数の呼び出し」
つまり,
そのため,
プローブ活性に応じて条件分岐させることで,プローブ呼び出し処理(引数準備含む)を抑止した方が,性能劣化が少ないのでは?
C 言語的に書くならば,
リスト3 プローブ無効化案(1)
/* プローブ有効時には probe_enabled を 1 に書き換え */
if(probe_enabled){
PROVIDER_PROBE(arg0, arg1, arg2, ....);
}
....
しかし,
そうなると,
プローブ活性に応じて無条件分岐命令を埋め込むことで,パイプラインを乱れさせることなくプローブ呼び出し処理を抑止できるのでは?
C言語的に書くならば,
リスト4 プローブ無効化案(2)
/* プローブ有効時には goto 文を nop に書き換え */
goto probe_disabled;
PROVIDER_PROBE(arg0, arg1, arg2, ....);
probe_disabled:
....
実行効率の点から見た場合,
今時のコンパイラの最適化処理に掛かると,
また,
以上のことから,
プローブ埋め込みによるオーバヘッドの計測
では実際のところ,
単純に実行時間で比較したのでは,
- 独自プローブに渡す引数は6つ
- プローブ非活性状態で,
引数準備処理だけを1012回繰り返す - アセンブラレベルで引数準備処理を除外したものを同じように1012回繰り返す
- 実行時間の差分を取る
- ループ1回あたりの性能劣化をクロック数に変換
以下の2つのCPUで計測しました。
- UltraSPARC IIIi
(1. 0GHz) - AMD PhenomII X4 905e
(2. 5GHz)
計測結果を以下に示します。
表1 プローブ埋め込みによる遅延の計測
環境 | 性能低下 | 引数渡しの方式 |
---|---|---|
SPARC | 2. | レジスタ |
x86 | 6. | スタック |
x86 | 0. | レジスタ |
SPARC アーキテクチャや 64bit x86(AMD64)における性能劣化が小さいのは,
6つの引数を積むオーバヘッドがそれぞれ2.
その一方で,
(CPU クロックと比較して)
この結果を見る限りでは,
なおここでの計測結果は,
- ※2)
- AMD64形式アプリケーションのABIがレジスタ渡しを採用していることは,
DTrace Day 2010. 03に参加された方から教えていただきました。ありがとうございます。