IA-32におけるパフォーマンスモニタとPEBSの使い方
1/27/03
Hiro Yoshioka
1. IA-32におけるパフォーマンスモニタリングファシリティ
Intel社の32ビットマイクロプロセッサ(IA-32と記す)はモデル固有のハードウェ
ア・パフォーマンス・モニタリング機能を持つ。ここでは簡単にその機能につい
て紹介する。
パフォーマンスカウンタ(PMC)はPentiumから実装され、さまざまなハードウェア
イベントの計測を可能としている。計測できるイベントはアーキテクチャモデル
によってことなる。最新のPentium 4およびIntel Xeonプロセッサでは、18個の
40ビットのパフォーマンスカウンタを持ち、多くのイベントを同時に計測するこ
とができるようになった。(計測できるイベント例:分岐命令数、分岐予測失敗
数、バストランザクション数、キャッシュミス数、TLBミス数、実行命令数等々
多数)
タイムスタンプカウンタ(TSC)は、ハードウェアリセット時に0から開始し、プロ
セッサのクロックサイクル毎に増加する64ビットのレジスタである。RDTSC命令
によって、カウンタの値を読む。(非特権命令)。ユーザーモードからも読めるの
で、簡単に実行時のクロック数を計測するのに利用できる。例えば、あるルーチ
ンの実行コストは、入口と出口でTSCを読み、その差が実行コスト(サイクル数
)である。
--------------
/* rdtsc命令の利用例*/
#define rdtscll(val) \
__asm__ __volatile__("rdtsc" : "=A" (val))
unsigned long long before;
unsigned long long after;
rdtscll(before);
/* 計測する部分*/
rdtscll(after);
diff=after-before; /* 実行クロック数 */
--------------
パフォーマンスカウンタはPentiumから実装されたが、以下に示すようにいくつ
かの限界があった。
- 同時に計測できるイベント数が少ない(Pentium IIIで2つ)
- リタイアせずにキャンセルされた命令のイベントも計測する
- イベントサンプリングの精度(粒度)が荒い
Pentium III(P6アーキテクチャと呼ばれている)系プロセッサは投機的な実行を
おこなう。したがって分岐予測がはずれた場合、命令をキャンセルするが、キャ
ンセルされた命令が引き起こしたイベントもカウントする。予測がはず
れた側の命令が引き起こしたイベント(例えばL2キャッシュミス)の回数も数えて
しまう。この場合、当該イベントが多数発生するのは、分岐予測がはずれたのが
主な原因なのか、それとも、L2キャッシュミスを多発させるようなプログラムな
のかは、この情報だけでは判断できない。プログラムを改良する時、分岐予測が
はずれないように改良するべきか、それともL2キャッシュミスを減らすような改
良をするべきか、どちらにプライオリティを置くかなどが判断できないのである。
最近のプロセッサは、(1)深いパイプラインを持つ、(2)投機的な実行をする、
(3)スーパースカラで同時に複数命令を実行する、(4)アウトオブオーダー実行す
る、などの特徴を持つ。このため、イベントサンプリングをする場合、ある回数
毎の例外処理ルーチンが起動され実際のプログラムカウンタ(PC)や各種レジスタ
の情報を収集する時点では、例外処理ルーチンのレイテンシおよび上記のプロセッ
サの特性により、取得したPCの値は実際のイベントを発生させたPCよりかなり先
を行っている。 Dean (\cite{dean})らの報告によると、PentiumProで、取得し
たPCと実際のPCは25命令以上隔たって分布した。Sprunt (\cite{sprunt}) の報
告によればPentium 4では65命令以上実際の命令と隔たってサンプルされた。プ
ロセッサがより深いパイプラインを持つようになればなるほど、この隔たりは広
くなると予想される。
このようにイベントの発生の場所を正確に特定できないため、イベントが発生し
ている時点の正確なコンテキストを入手できない。イベントが発生していること
はわかっても、正確なコンテキストがわからないので、性能上何が問題か特定
することはこれらの情報だけでは困難である。
イベントの発生の正確な特定と、その時点での精密なコンテキストの入手が求め
られている。
上記の問題を解決するために、Pentium 4/Intel Xeonプロセッサでは下記のよう
な拡張を行っている。
1.1 Pentium 4/Intel Xeonでの拡張
1.1.1多くのパフォーマンスカウンタ
次のようなパフォーマンスモニタリングファシリティを持つ。
- ESCR (Event Selection Control) 45個
- PMC (Performance Monitoring Counter) 18個
- CCCR (Cunter Configuration Control) 18個
- DS (Debug Store)
P6系プロセッサに比べ、同時に計測できるPMCの数が、2個から18個に増えている。
ESCRに、計測すべきイベント(例;L1 cache miss/TLB missなど)を、CCCRに計測
方法(イベントのフィルタリング、割り込みの制御方法など)と計測すべきイベン
トを設定したESCRを指定し計測を開始するとイベント数がPMCに格納される。
1.1.2 At-Retirement計測
At-Retirement計測というのは、実際にコミットされた命令にまつわるイベント
(non-bogusないしretireと呼ぶ)と、投機的に実行されコミットされなかった命
令(最終的にキャンセルされた命令)に関するイベント(bogusと呼ぶ)にタグをつ
け、命令のretirement時に区別する機能を指す。(投機的に実行した命令を確定
することをretirementするという)
パフォーマンスカウンタはbogusないしnon-bogusを区別して計測できる。
従来は、実際にコミット(リタイヤ)した命令のみならず、キャンセルした命令に
よって引きおこされたイベントもカウントしてしまっていた。
より粒度の細かい計測を可能にするために、Pentium 4/Intel Xeonプロセッサで
は、イベントにタグを付け、コミットした命令だけ(あるいはキャンセルされた
命令だけ)を計測する機能を提供した。この機能のことをAt-Retirement計測と呼ぶ。
1.1.3 Pentium 4/Intel Xeonにおける精密なイベントサンプリング
最近のプロセッサは、(1)深いパイプラインを持つ、(2)投機的な実行をする、
(3)スーパースカラで同時に複数命令を実行する、(4)アウトオブオーダー実行す
る、などの特徴を持つため、サンプリングしたプログラムカウンタ(PC)と実際に
イベントを発生させたPCが一致しないという問題があった。
この問題を解決するためにPentium 4系プロセッサで精密なイベントサンプリン
グ(PEBS -- Precise Event Based Sampling)という機能が実装された。
カーネル空間にデバッグストア(DS -- Debug Store)領域というのを確保できる。
パフォマンスカウンタが PEBS 用に設定されている場合、カウンタのオーバフロー
が発生する都度、プロセッサは汎用レジスタ、EFLAGSレジスタ、インストラクショ
ンポインタをDS領域にあるPEBS バッファのPEBSレコードに自動的に格納する。
これはマイクロコードによって行なわれる(ハードウェアが自動的に実行する)の
で、プロセッサの精密な状態を保存が可能となっている。そしてプロセッサはパ
フォーマンスカウンタの値をリセットし、カウンタをリスタートする。DS領域に閾
値を設定しておくと、それを越えるレコードが格納された時、PMI割り込みが発
生するので、DS領域のデータをユーザー空間に退避するようなデバイスドライバ
を用意しておく。
PMCは40ビットなので、$2^{40}$回イベントを計測するとオーバーフローする。
これを利用して、PMCにあらかじめ2の補数をセットしておけば、その回数毎にイ
ベントをサンプリングできるのである。例えば、-100をPMCにセットすれば、
100回目にオーバフローが発生するので、PEBSの機能(ハードウェアの機能)によっ
て、イベントが発生したプログラムカウンタ(PC)、各種レジスタの値などを保存
される。これをパフォーマンスイベントサンプリングと呼ぶ。
PEBSはAt-Retirementイベントのサブセットだけをサポートしている。
2. PEBSの利用例
2.1 準備
パフォーマンスモニタリングファシリティの機能はLinuxではデフォルトで利
用できないので、ここではabyssのパッチでPEBSの機能を利用することにする。
http://www.eg.bucknell.edu/~bsprunt/
マシン:PEBSはPentium 4ないしIntel Xeonプロセッサが必須である。
Linux Kernel 2.4.18など
2.2 インストール
1) Linux Kernel
www.kernel.orgなどから linux kernel 2.4.18 を download
$ tar xvzf linux-2.4.18.tar.gz
2) brink_abyssのインストール
$ tar xvzf brink_abyss_release_2.0.tar.gz
Install.shtmに従ってインストールをする
kernel patchをあてる
$ cd linux-2.4.18
$ patch -p5 ../brink_abyss/ebs_kernel_patches/ebs-kernel-patch-2.4.18
3) Linux Kernel build
$ make mrproper
$ make xconfig
ここで、Processor family に Pentium 4を選択
SMP は no
Local APIC support on uniprocessor 選択
$ make dep
$ make bzImage
rootで
# cp arch/i386/boot/bzImage /boot/vmlinuz-2.4.18-ebs
# cp System.map /boot/System.map-2.4.18-ebs
# make modules
# make modules_install
liloないしgrubなどの設定をする。
4) abyss デバイスドライバ作成
./abyss_dev/Makefileを編集する。INC_FLAGSに先にインストールした
kernelのincludeディレクトリを示すようにしておく。
# Change the include path below to point to the linux include
# directory for the Linux 2.4 kernel you built with the EBS
# patch.
#
# INC_FLAGS = -I/home/kernel/linux-2.4.17-ebs/linux/include/
INC_FLAGS = -I/usr/src/linux-2.4.18_ebs/include/
$ cd abyss_dev
$ make
$ su
# mknod /dev/abyss c 10 243
# chmod 666 /dev/abyss
# insmod abyss_dev.o
rebootするたびにinsmodする必要がある。
5) abyss_front_endのインストール
$ cd abyss_fron_end
$ make
6) brinkのインストール
brinkはPentium 4の特性の記述や測定方法のセットアップなどをおこなう
Perlのスクリプトである。
必要なPerlモジュールはabyssのディストリビューションに添付されてい
るが自分の環境にあわない場合などは、
http://www.cpan.org
などがら入手する。
# Change the two definitions below to point to the locations where you
# installed the configuration file (usually "pentium4_emon.txt") and
# the abyss executable (usually "abyss").
#
$default_path_to_config_file = "./pentium4_emon.txt";
$default_path_to_abyss = "./abyss";
2.3 PEBSによる実行
上記の準備の後、以下のようにして実行する。
1) 設定ファイルの記述。
XMLで設定ファイルを記述する。
下記参照
-----------------------
-----------------------
プロセッサの型を指定する。ここではpentium4とする。
測定するプログラムを...で指定する。
-----------------------
-----------------------
デフォルトで測定するモードはモードとしている。
-----------------------
-----------------------
L2 cache missを測定する。インターバルは10000回ごとに一回サンプリングす
る。その時全てのレジスタの値を取得する。(sample="A")。3000サンプルまで
バッファリングする。(buffer="3000")。それ以上はabyss ドライバーがユー
ザー空間へコピーをする。最大200000サンプルまで取得し、それを越えた場合
はエラーで終了する。
-----------------------
-----------------------
実行命令数、および実行マイクロ命令数を計測する
上記の設定ファイル名をpebs_pgbench.txtとすると
$ ./brink -exp pebs_pgbench.tx -outdir emon_data
でデータ収集できる。
2.4 データ分析
-outdirで指定したディレクトリに各種サンプリングデータが収集されている。
ebs (event based sampling)でsample="A"の場合下記のようなデータが
〜ebs_samples.txt
というファイルに格納される。
#
# Date: Sun Jan 5 14:07:08 2003
# Command: /usr/bin/time --output=./emon_data01/job.ld_miss_2L_nop.pebs_eip1/job.ld_miss_2L_nop.pebs
_eip1.time.txt -f "percent_cpu=%P, os_seconds=%S, user_seconds=%U, elapsed_seconds=%e, major_page_fa
ults=%F, minor_page_faults=%R, swaps=%W, exit_status=%x" /usr/local/pgsql/bin/pgbench -c 10 -t 1000
pgbench >/dev/null
#
# EBS Configuration:
# EBS type: precise
# CCCR MSR address: 370
# Counter MSR address: 310
# events per sample: 10000
# samples to buffer: 3000
# sample type: ALL
# max samples: 200000
# samples collected: 11380
# dropped samples: 0
# sample filename: ./emon_data01/job.ld_miss_2L_nop.pebs_eip1/job.ld_miss_2L_nop.pebs_eip1.eb
s_samples.txt
#
# Each sample is: {eflags, linear_ip, eax, ebx, ecx, edx, esi, edi, ebp, esp}
#
# eflags linear_ip eax ebx ecx edx esi edi ebp esp
#
00200206 40008e37 40015588 40014dc0 00000000 40015abc 40029f9c 40015abc bffff314 bffff23c
00200246 400087b5 40015588 40014dc0 bffff3fc 40015b24 00000000 40016ac4 bffff404 bffff37c
00200293 08076769 00000001 4037af1c 4037af1c 08284ed0 00000001 0825bc50 bfffe500 bfffe4b8
00200282 080770ba 00000013 08284fd8 4036e91c 4036e978 00000032 00000000 bfffe9b0 bfffe998
00003202 0865a37c 02a9000e 00550036 0866e6d0 00000010 0000000e 00000055 bffff50c bffff4c4
00200202 4000c20d 40095a8c 40014dc0 0000e8f8 40087b90 400554a0 40088080 bfffefa8 bfffef70
00200246 40008eda 000544ee 40014dc0 400339c2 000001d2 40015bd0 401fe088 bfffe7a8 bfffe6d0
00200286 c0121f74 f28540a0 08265000 00000000 e464e000 f2854084 c0111e18 08265000 e464ff08
00200246 0811ef12 0000000f 4038ebc8 4039ec0c 000007a2 bfffea58 0816bdf4 bfffea18 bfffea10
00200282 c012456a 080c9000 ea95f954 ea95f954 c02a86b4 f2854e04 00000000 f38c6e8c e44c9ea0
00200202 4014b681 401f7344 401f8cc4 0000016c 00000032 0823dfa8 081f4750 bfffe7b0 bfffe788
00200286 400090d7 ffffffff 40014dc0 0805ba61 40055b3f 40055b30 0805ba5d bfffeef8 bfffee20
00200246 08086ea7 081fbdb4 bfffec50 0825b840 bfffec60 0000014f 082904d8 bfffec60 bfffec30
00200246 08082a55 08265628 00000001 00000000 00000001 08291538 403344c8 bfffeb10 bfffeae8
以下略
2桁目のliner_ip(リニアアドレス=論理アドレス)でソートすれば当該イベン
ト(この場合はL2 cache miss)が多発している論理アドレスがわかる。
$ egrep -v '#' ファイル名|awk '{print $2}'|sort|uniq -c|sort -nr|head
でトップ10リストができる。
アドレスとルーチン名の対応付け
例えば測定したアプリケーションがpostgresの場合、あらかじめ
--enable-debug で configure しておけば、デバッグシンボル付きでコンパイ
ル、ビルドする。(gcc -g)
できあがった実行オブジェクトを逆アセンブル(objdump -S)すれば、アドレス
と当該ルーチン名との対応が付く。
eip2rというeip_listとmapの対応をつけるスクリプトを用意したので、それを
利用すれば簡単に対応がつく。
http://sourceforge.jp/projects/hardmeter
のCVSから入手可能。
eip2rはlinux kernelのSystem.mapの形式を期待しているので、
$ objdump -S src/backend/postgres |grep '^0[0-9]*'\
|awk '{print $1 " T " $2}' > mapfile
マップファイルを用意しておく。
注:nmでも同様なことができるか。
$ nm -n
下記でトップ10リストができる。
$ eip2r 5eipL1 /usr/src/postgresql-7.3cc/p.map.icc 2>/dev/null|sort |uniq -c|sort -nr|head
39355 08106074 :
38071 08106051 :
36463 08106395 :
26885 081062e9 :
22467 081062c2 :
17720 08053fef :
16840 0815e893 :
16555 0816a1d5 :
15026 080574b9 :
12803 080602b9 <_bt_isequal>:
あとはトップ10リストの上位からソースコードなどを確認しつつチューニング
をする。