演算に要するクロック数の比較


私の研究室では,「コンピュータによる熱移動と流れの数値解析」(スハス V. パタンカー原著, 水谷幸夫,香月正司共訳)というテキストを使ったゼミを週一回行っている. ある日のゼミで,このテキストの中の "指数計算には費用がかかる" という意味の記述について, 学生から「計算に費用がかかるとはどういうことか?」という質問が出た. 一人に一台,十分高性能なコンピュータが与えられており,いつでも好きなだけ計算を流すことができる現在の 学生にとっては,メインフレームにリモートログインして計算し,そのときのCPUの使用時間に応じて課金されていた 昔のことが理解できないのも無理はない.

その学生にはこのような昔の窮状を説明することで納得してもらったが,実際問題として,指数(べき)計算は, 四則演算に比べてどのくらい手数がかかるのだろうか.

そこで,CPUのクロックを測定するに書いたRTDSC命令を使って, 各演算に要したクロック数を測定してみた. この命令はPentium以降のCPUでサポートされており,マシンが起動してからの総クロック数を得るものである. 先のページでは,1秒間隔で2回この命令を呼び出すことによりCPUのクロックを測定した. なお,RTDSC命令を呼び出すためのAPIやDelphiの内部関数は無いので,呼出しにはアセンブラを使う必要がある.

蛇足だが,「コンピュータによる...」は名著であり,流体の数値解析を学ぶ際には大変お勧めのテキストである. ゼミで使っているのは邦訳であるが,是非原著を読んでみたいという気にさせられる.

さて,演算に要したクロック数の測定であるが,以下のようなコードを書いてみた.

procedure TForm1.Button1Click(Sender: TObject);
var
  X, Y, Z: Double;
  Count, Clock, TotalClock, MeanClock, StartLo, StartHi, FinishLo, FinishHi: DWORD;
  Start, Finish: Int64;
begin
   Randomize;
   TotalClock := 0;
   for Count := 1 to 10 do
   begin
      X := Random;  // 乱数を生成
      Y := Random;  // 乱数を生成
      
      // 演算前のクロック数を取得
      asm
        PUSH edx
        PUSH eax
        DW   $310F
        MOV  StartLo, eax
        MOV  StartHi, edx
        POP  eax
        POP  edx
      end;
      
      // 演算を実行
      Z := X + Y;

      // 演算後のクロック数を取得
      asm
        PUSH edx
        PUSH eax
        DW   $310F
        MOV  FinishLo, eax
        MOV  FinishHi, edx
        POP  eax
        POP  edx
      end;
      
      // クロック数の差を求める
      Start := StartHi;
      Start := StartLo or Start shl 32;
      Finish := FinishHi;
      Finish := FinishLo or Finish shl 32;
      Clock := Finish - Start;
      Inc( TotalClock, Clock );
   end;
   ClockLabel.Caption := IntToStr( Round( TotalClock / 10 ) );
end;

この例は浮動小数点数の加算に要するクロックを測定するものである.

      Z := X + Y;

の前後でクロック数を測定し,両者の差を加算に要したクロックとする. 他の演算を測定する場合はこの行をZ:=X-Yのように書き換える. 測定は10回を行い,その平均を取る.

厳密に言えば,上のコードでは演算に要した正味のクロック数は測定できない. なぜなら,Z:=X+Yのような単純な処理(代入文)でさえ,加算の他にXやYの値をメモリからレジスタに吸い上げたり, 演算結果をメモリ上のZに書き込む操作が含まれているためである. したがって,演算に要した正味のクロックは測定されたクロックよりも若干少なくなる. ただし,レジスタと変数の間で値をコピーする手間はどの演算でもだいたい同じであるから, 測定値をそのまま比較してもそれなりの傾向は得られるはずである.

注意すべき点は,RTDSCを呼び出す前にedxレジスタとeaxレジスタの値をスタックに退避させておき, 呼出し後に再び両レジスタに書き戻す操作が必要があるということ.そうしないと,それまで両レジスタに 入っていた値がRTDSCの呼び出しによって消されてしまう. 実際,レジスタの値を退避させなければ演算の結果がおかしくなる. CPUのクロックを測定するでは,単にRTDSC呼出し後のレジスタの値を取得するだけだったので このような操作は必要なかった.

なお,スタックはFILO(First In, Last Out,先入れ後だし)のデータ構造であるから,

    PUSH edx
    PUSH eax

とプッシュしたら,

    POP  eax
    POP  edx

とポップする.つまり,後にプッシュしたものから順にポップしなければならない.

測定はPentimIII 600MHzのマシンを使って 浮動小数点演算と整数演算との両方に対して行った.まず浮動小数点演算の結果を示す.

浮動小数点数
演算 要したクロック数
加算 Z := X + Y 41
減算 Z := X - Y 41
乗算 Z := X * Y 43
除算 Z := X / Y 70
整数べきZ := IntPower( X, Y )175
実数べきZ := Power( X, Y ) 538

加算,減算および乗算の必要クロック数はほぼ同等で,除算はこれらの約2倍程度だ. 除算には除数が0か否かの判定が絡んでくるためだろう.

予想通り,べきの計算は四則演算に比べて相当多くのクロックを要している. 結果を単純に比較すれば,実数べきには加減算の10倍以上のクロックを要することになる. 1クロックあたりの経過時間が一定ならば,計算時間も10倍かかることになる. ただし,IntPowerの必要クロックはPowerのそれに比べてかなり少ないので,整数乗の計算にはIntPowerを使うことで かなり計算時間を減らすことができる.

次に整数演算の結果を示そう.

整数
演算 要したクロック数 備考
加算 Z := X + Y 38
減算 Z := X - Y 38
乗算 Z := X * Y 40
除算 Z := X / Y 74 Zは浮動小数点数
整数べきZ := IntPower( X, Y )195 X,Yは整数,Zは浮動小数点数
実数べきZ := Power( X, Y ) 548 X,Yは整数,Zは浮動小数点数

IntPowerおよびPowerのプロトタイプは

function IntPower(Base: Extended; Exponent: Integer): Extended;
function Power(Base, Exponent: Extended): Extended;

となっており,実引数Xが整数であっても浮動小数点数に変換されたのちこれらの関数に渡される. 浮動小数点数の結果と同じく,べきの計算は四則演算に比べて必要クロック数が確かに多いが, 上の結果にはこのような型変換に要するクロックも含まれており,やや冗長的かも知れない.


お問い合わせはメールにて: akasaka@klc.ac.jp

戻る
SEO [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送