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をアトミックに読み出すことができないため、ちょうど下位から上位に桁上がりがあった場合にはもう一度読み直すということをしています。



トラックバックURL

コメント一覧

1. Posted by kazuya   2009年08月30日 00:02
手元にppc32環境がないので確認はしていないのですが、最初のmftbuはdo...whileループの中にないといけないのではないでしようか。
2. Posted by koba   2009年08月31日 09:48
ありがとうございます。ご指摘のとおりです。
ソースを修正します。

コメントする

名前
URL
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード