[PR]恋愛の悩みなら:こころtoからだで診断!

- read-time stamp counter (RDTSC)命令の利用方法 -

yosirin-hello / yosi-s@galaxy.ocn.ne.jp

1. はじめに

 自分が書いたプログラムが一体どれくらいクロックサイクルを消費しているのか気になったことはありませんか? もしそれが知ることができれば, 最適化前と後とでクロックサイクルを比較すればどの程度高速化したかはっきりわかるようになります. そこで今回はRDTSC命令でクロックサイクルを計測する方法を紹介します.
2. RDTSC命令とは

RDTSC命令で, 現在のクロックサイクル数を取得することができます.

List1. 単に現在のクロックサイクル数を取得するプログラム
LARGE_INTEGER cycles; // (1)

__asm {
    cpuid // (2)
    rdtsc // (3)

    mov cycles.LowPart,  eax // (4)
    mov cycles.HighPart, edx
}

printf("現在のクロックサイクル数 : %d", cycles.QuadPart );

(1)LARGE_INTEGERは64ビットの変数です. 詳しくはMSDNライブラリーを参照して下さい.
別ににクロックサイクル数を格納する変数に32ビットの変数を使ってもよいのですが, 32ビットではオーバーフローを起こす可能性があるので, ここでは64ビットの変数を使用しました.今や, GHzの時代と言われていますから, 32ビットではすぐにオーバーフローしてしまう可能性があります.

(2) RDTSC命令を実行する前にシリアライジング命令(ここではCPUID命令)を実行することで正確なクロックサイクル数を取得できるようになります.
CPUID命令は, PentiumプロセッサとMMX Pentiumプロセッサで実行する場合必要ありません.

(3) RDTSC命令を実行すると, EAXレジスタにクロックサイクル数の下位32ビットが, EDXレジスタにクロックサイクル数の上位32ビットが格納されます.

(4) MOV命令でEAXとEDXの内容をcyclesに転送します.
3. 関数のクロックサイクル数の計測

クロックサイクル数を調べたい関数の呼び出し前と後にList1.を使えば, どれだけのクロックサイクルを消費したのか調べることができます.

List2. ある関数が消費したクロックサイクル数を計測するプログラム
LARGE_INTEGER cycles;

__asm {
    cpuid
    rdtsc

    mov cycles.LowPart,  eax // (1)
    mov cycles.HighPart, edx
}

// 関数の呼び出し

__asm {
    cpuid
    rdtsc

    sub eax, cycles.LowPart  // (2)
    sub edx, cycles.HighPart

    mov cycles.LowPart,  eax // (3)
    mov cycles.HighPart, edx
} printf("関数が消費したのクロックサイクル数 : %d [cycles]", cycles.QuadPart );

(1) 現在のクロックサイクル数を格納します.

(2) 関数を呼び出した後のクロックサイクル数と, 関数を呼び出す前のクロックサイクル数との差をとります.

(3) MOV命令で関数が消費したクロックサイクル数を得たEAXとEDXの内容をcyclesに転送します.

落とし穴 : もし, "関数の呼び出し"の部分でEBXレジスタを使用する関数があると, デバッグモードでコンパイルしたときに"warning C4731: frame pointer register 'ebx' modified by inline assembly code"と表示されます. これを無視して実行するとアプリケーションエラーが発生してしまいます.

4. 具体例 : DotProduct()のクロックサイクル数の計測

それでは, 具体例として内積(dot product)を計算する関数が消費したクロックサイクル数を計測してみます.

List3. DotProduct()が消費したクロックサイクル数を計測するプログラム
#include <stdio.h>
#include <windows.h>

float DotProduct( float a[4], float b[4] )
{
        float result = 0;

        for( int i = 0; i < 4; i++ )
                result += ( a[i] * b[i] ) ;

        return result;
}

void main(void)
{
        LARGE_INTEGER cycles;
        float a[4] = { 1.0f, 2.0f, 3.0f, 1.0f };
        float b[4] = { 5.0f, 6.0f, 7.0f, 1.0f };
        float dot_prod;

        __asm {
                cpuid
                rdtsc
                mov cycles.LowPart,  eax
                mov cycles.HighPart, edx
        }

        dot_prod = DotProduct( a, b ); // (1)

        __asm {
                cpuid
                rdtsc
                sub eax, cycles.LowPart
                sub edx, cycles.HighPart

                mov cycles.LowPart, eax
                mov cycles.HighPart, edx        
        }
        printf("DotProduct()が消費したのクロックサイクル数 : %d\n [cycles]", cycles.QuadPart );
}
List3. の実行結果
DotProduct()が消費したのクロックサイクル数 : 696 [cycles]
Press any key to continue

(1) 内積を求める関数です.

5. 参考文献

1. Using the RDTSC Instruction for Performance Monitoring,
http://cedar.intel.com/software/idap/media/pdf/rdtscpm1.pdf
2. Microsoft MSDN Library
6. 改訂履歴

改訂 履歴 日付
1.0 初公開 02/12/2002


Copyright © 2002 yosirin-hello All rights reserved.


[PR]中古車探しは、ガリバー:在庫多数、全車保証つき!