2009年08月19日
PowerPCのタイムベースレジスタを利用した簡単な実行時間の計測方法
PowerPCにはCPUのクロックサイクルでカウントアップされるタイムベースレジスタというカウンタがあります。これを利用して簡単にプログラムの2点間の時間を計測することができます。
ある関数を複数の方法で実装して、どちらが速いかを比較する場合に便利です。
手軽に使えるようにソースコードを一つのヘッダファイルにまとめてみました。
タイムベースレジスタについて
タイプベースレジスタはCPUのクロックサイクルでカウントアップされます。上位、下位それぞれTBU, TBLという32bitのレジスタに分かれていて、全体で64bitのカウンタになっています。
TBU, TBLへの書き込みは特権モードでしかできませんが、読み込みはユーザーモードでもできます。
TBL(下位32bit)だけだと約8秒で一周していまいますが、TBUと組み合わせて64bitにすることで、8 * 40億 秒 (= 1000年以上) 持ちます。なので使用前にゼロにリセットする必要もありません。
ソースコード
カウンタの読み出しの時のオーバーヘッドを少なくするためインライン関数にしています。
gccの拡張記法を使用しています。
#ifndef __KMC_PMON_PPC_H
#define __KMC_PMON_PPC_H
volatile __inline__ static unsigned long long __attribute__((always_inline))
pmon_read_tb()
{
unsigned long long ret;
unsigned long x, y, z;
do {
asm volatile ("mftbu %0" : "=r" (z));
asm volatile ("mftbl %0" : "=r" (x));
asm volatile ("mftbu %0" : "=r" (y));
} while (z != y);
ret = y;
ret = (ret << 32) | x;
return ret;
}
/* Supervisor mode only */
__inline__ static void __attribute__((always_inline))
pmon_reset_tb()
{
unsigned long x = 0;
asm volatile ("mttbl %0" :: "r" (x));
asm volatile ("mttbu %0" :: "r" (x));
}
#endif /* __KMC_PMON_PPC_H */
使用方法
pmon_ppc.h をインクルードし、(#include "pmon_ppc.h")
任意のところでpmon_read_tb()でサイクルカウンタを読み出し、その差分で時間を計ります。
pmon_read_tb()の型はunsigned long long (符号なし64bit)です。
ライブラリなどを追加する必要がないので、Makefile等の変更が不要です。
計測したい部分のソースファイルだけの変更で使えます。
使用例
#include <stdio.h>
#include "pmon_ppc.h"
int func(int x)
{
int sum = 0;
int i;
for (i = 1; i <= x; i++) {
sum += i;
}
return sum;
}
int
main()
{
unsigned long long start, end;
int x;
start = pmon_read_tb();
x = func(1000);
end = pmon_read_tb();
printf("time = %lld, x = %d\n", end - start, x);
return x;
}
(2009.8.31 追記)
コメントで誤りを指摘していだだきした。
コードを以下のように修正しました。
修正前
asm volatile ("mftbu %0" : "=r" (z));
do {
asm volatile ("mftbl %0" : "=r" (x));
asm volatile ("mftbu %0" : "=r" (y));
} while (z != y);
修正後
do {
asm volatile ("mftbu %0" : "=r" (z));
asm volatile ("mftbl %0" : "=r" (x));
asm volatile ("mftbu %0" : "=r" (y));
} while (z != y);
これは64bitのカウンタの上位32bitと下位32bitをアトミックに読み出すことができないため、ちょうど下位から上位に桁上がりがあった場合にはもう一度読み直すということをしています。