CPUのクロック周波数を計算

今回はちょっとお遊びです(^^;
インテルのWebサイトからダウンロードしてきたデベロッパーズマニュアルを覗いていたら、「クロック周波数を計算できねーかな?」と思って作ってみました。興味がない方は時間の無駄ですので、他のページに飛んだ方がいいです(笑)

さて能書きはこれくらいにして...まず、クロック周波数の計算を行う前にやっておかなければならないことがあります。CPUID命令が使えるか調べ、CPUID命令を実行してRDTSC命令が使えるか調べなくてはなりません。そのための関数を二つ作成します。

CPUID命令が使えるかどうかを調べるには、EFLAGSのビット21を設定できるか否かで判断できます。それを調べるのがEnabledCPUID関数です。
RDTSC命令が使えるかを調べるにはEAXレジスタに$01を設定しCPUID命令を実行します。EDXレジスタにはCPUの機能情報が入ります。RDTSCが使える場合EDXレジスタのビット4が1に設定されるので、それを調べればよいわけです。

function EnabledCPUID: Boolean; assembler;
//CPUID命令が使えるか調べる関数
asm
  PUSH    EDX          //念のために退避
  PUSH    ECX          //念のために退避
  XOR     EAX, EAX     //ゼロクリア
  PUSHFD               //EFLAGSをスタックにプッシュ
  POP     EDX          //EDXにロード
  MOV     ECX, EDX     //比較のためにECXに退避
  XOR     EDX, $200000 //ビット21を設定
  PUSH    EDX          //スタックにプッシュ
  POPFD                //EFLAGSに読み込ませる
  PUSHFD               //EFLAGSをスタックにプッシュ
  POP     EDX          //EDXにロード
  XOR     EDX, ECX     //ビット21がクリアされているか調べる
  SETNZ   AL           //ZF = 0の場合CPUIDが使える
  POP     ECX          //元の内容に戻す
  POP     EDX          //元の内容に戻す
end;

function EnabledRDTSC: Boolean; assembler;
//RDTSC命令が使えるか調べる関数
asm
     CALL  EnabledCPUID //CPUIDは使える?(上の関数を呼び出す)
     CMP   EAX, $01     //戻り値を比較
     JE    @@1          //Trueの場合@@1にジャンプ
     XOR   EAX, EAX     //使えねー
     RET                //ここで関数を終了
@@1://CPUIDが使える
     PUSH  ECX          //念のために退避
     PUSH  EDX          //念のために退避
     PUSH  EBX          //EBXは保護しないといけないので退避
     XOR   EAX, EAX     //EAXをゼロクリア
     DW    $A20F        //CPUID
     CMP   EAX, $01     //最大入力値が1以上あるか?
     JB    @@2          //ない(つまりEAX=0ちゅーこと)
     MOV   EAX, $01     //あるのでEAXを1にして...
     DW    $A20F        //CPUIDを実行
     XOR   EAX, EAX     //EAXをゼロクリア(戻り値として使う)
     TEST  EDX, $10     //ビット4が設定されてる?
     SETNZ AL           //ZFを反転して転送
     JMP   @@3          //終わり
@@2://RDTSCは使えない
     XOR   EAX, EAX
@@3://後始末
     POP   EBX          //スタックに退避したEBXを戻す
     POP   EDX          //元の内容に戻す
     POP   ECX          //元の内容に戻す
end;

次にCPUクロックを計算する関数です。InitCPUClockはアプリケーションに起動時にでも呼び出してください。

var
  StartTime: Cardinal = 0;
  StartTSC : Int64Rec;
  StartTSC64: Int64 absolute StartTSC;

function InitCPUClock: Boolean;
//CPUクロックの計算に必要な変数などを初期化する
begin
  Result := EnabledRDTSC;
  if Result then
  begin
    StartTime := GetTickCount;
    asm
      PUSH EDX   //念のために退避
      PUSH EAX   //念のために退避
      DW   $310F //RDTSC
      MOV  DWORD PTR [StartTSC.Hi], EDX
      MOV  DWORD PTR [StartTSC.Lo], EAX
      POP  EAX   //元の内容に戻す
      POP  EDX   //元の内容に戻す
    end;
  end;
end;


function GetCPUClock: Double;
//CPUクロックを計算する
var
  CurrTSC: Int64Rec;
  CurrTSC64: Int64 absolute CurrTSC;
begin
  if StartTime > 0 then
  begin
    asm
      PUSH  EDX   //念のために退避
      PUSH  EAX   //念のために退避
      DW    $310F //RDTSC
      MOV   DWORD PTR [CurrTSC.Hi],  EDX
      MOV   DWORD PTR [CurrTSC.Lo],  EAX
      POP   EAX   //元の内容に戻す
      POP   EDX   //元の内容に戻す
    end;
    Result := ((CurrTSC64 - StartTSC64) /
              (GetTickCount - StartTime)) / 1000; // MHz
  end else
    Result := 0;
end;

Copyright 2001 Rinka Kouzuki All Rights Reserved.