35

関数の手順など、ルーチンの処理にかかった時間を決定するための最良かつ最も正確な方法は何ですか?

現在、アプリケーションのいくつかの機能を最適化しようとしているため、変更をテストするときに、改善があったかどうかを確認するだけでは判断が難しいため、質問します。したがって、ルーチンの処理にかかった正確な、またはほぼ正確な時間を返すことができれば、コードに何らかの変更が加えられた場合に、どの程度うまくいったかについてより明確な考えが得られます。

GetTickCount を使用することを検討しましたが、これがほぼ正確かどうかはわかりません。

ルーチンの時間を計算するために再利用可能な関数/手順を用意し、次のように使用すると便利です。

// < prepare for calcuation of code
...
ExecuteSomeCode; // < code to test
...
// < stop calcuating code and return time it took to process

いくつかの提案を聞くのを楽しみにしています。

ありがとう。

クレイグ。

4

8 に答える 8

24

私の知る限り、最も正確な方法はQueryPerformanceFrequencyを使用することです。

コード:

var
  Freq, StartCount, StopCount: Int64;
  TimingSeconds: real;
begin
  QueryPerformanceFrequency(Freq);
  QueryPerformanceCounter(StartCount);
  // Execute process that you want to time: ...
  QueryPerformanceCounter(StopCount);
  TimingSeconds := (StopCount - StartCount) / Freq;
  // Display timing: ... 
end; 
于 2011-05-17T12:26:32.720 に答える
18

Eric Grange のSampling Profilerを試してみてください。

于 2011-05-17T12:20:44.820 に答える
14

Delphi 6 以降では、x86 タイムスタンプ カウンターを使用できます。
これは CPU サイクルをカウントします。1 Ghz プロセッサでは、各カウントに 1 ナノ秒かかります。
それ以上の正確さは得られません。

function RDTSC: Int64; assembler;
asm
  // RDTSC can be executed out of order, so the pipeline needs to be flushed
  // to prevent RDTSC from executing before your code is finished.  
  // Flush the pipeline
  XOR eax, eax
  PUSH EBX
  CPUID
  POP EBX
  RDTSC  //Get the CPU's time stamp counter.
end;

x64 では、次のコードは の遅延の影響を受けないため、より正確ですCPUID

  rdtscp        // On x64 we can use the serializing version of RDTSC
  push rbx      // Serialize the code after, to avoid OoO sneaking in
  push rax      // subsequent instructions prior to executing RDTSCP.
  push rdx      // See: http://www.intel.de/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-benchmark-code-execution-paper.pdf
  xor eax,eax
  cpuid
  pop rdx
  pop rax
  pop rbx
  shl rdx,32
  or rax,rdx

上記のコードを使用して、コードの実行前後のタイムスタンプを取得します。
可能な限り最も正確な方法で、パイのように簡単です。

良い結果を得るには、テストを少なくとも 10 回実行する必要があることに注意してください。最初のパスではキャッシュがコールドになり、ランダムなハードディスクの読み取りと割り込みによりタイミングがずれることがあります。
このことは非常に正確であるため、最初の実行のみを計ると、間違った考えを与える可能性があります.

QueryPerformanceCounter() を使用しない理由は
QueryPerformanceCounter()、CPU の速度が低下した場合に同じ時間を与え、 CPU のスロットリングを補正します。過熱などでCPUの速度が低下した場合、RDTSCは同じ量のサイクルを提供します。
したがって、CPU が過熱し始めて減速する必要がある場合、QueryPerformanceCounter()はルーチンに時間がかかっていると言い(これは誤解を招きます)、RDTSC は同じ量のサイクルがかかると言う(これは正確です) .
これは、実時間ではなく、コードが使用する CPU サイクルの量に関心があるためです。

最新のインテル ドキュメントから: http://software.intel.com/en-us/articles/measure-code-sections-using-the-enhanced-timer/?wapkw=%28rdtsc%29

プロセッサ クロックの使用

このタイマーは非常に正確です。3 GHz プロセッサを搭載したシステムでは、このタイマーは 1 ナノ秒未満のイベントを測定できます。[...] ターゲット コードの実行中に周波数が変更された場合、最初と最後の読み取り値が同じクロック周波数を使用して取得されていないため、最終的な読み取り値は冗長になります。この間に発生したクロック ティックの数は正確ですが、経過時間は不明です。

RDTSC を使用しない場合
RDTSC は、基本的なタイミングに役立ちます。シングル CPU マシンでマルチスレッド コードのタイミングを計っている場合、RDTSC は問題なく動作します。複数の CPU がある場合、startcount は 1 つの CPU から取得され、endcount は別の CPU から取得される場合があります。
そのため、RDTSC を使用して、マルチ CPU マシンでマルチスレッド コードを計測しないでください。シングル CPU マシンでは正常に動作し、マルチ CPU マシンでもシングル スレッド コードは正常に動作します。
また、RDTSC は CPU サイクルをカウントすることにも注意してください。ディスク IO やネットワークなど、時間がかかるが CPU を使用しないものがある場合、RDTSC は適切なツールではありません。

しかし、ドキュメントによると、最新の CPU では RDTSC は正確ではありません。RDTSC は時間を追跡するためのツールではなく、CPU サイクルを追跡するためのツール
です
そのため、それは正確な唯一のツールです。CPU クロックは以前のように絶対的ではないため、時間を追跡するルーチンは最新の CPU では正確ではありません。

于 2011-05-17T13:19:11.277 に答える
10

Delphiのバージョンを指定しませんでしたが、DelphiXEのユニット診断でTStopWatchが宣言されています。これにより、実行時間を妥当な精度で測定できます。

uses
  Diagnostics;
var
  sw: TStopWatch;
begin
  sw := TStopWatch.StartNew;
  <dosomething>
  Writeln(Format('runtime: %d ms', [sw.ElapsedMilliseconds]));
end;
于 2011-05-17T13:27:05.393 に答える
7

現在、いくつかの機能を最適化しようとしているため、質問します

測定とは、何を最適化すべきかを見つける方法だと考えるのは当然ですが、もっと良い方法があります。

何かを最適化する価値があるのに十分な時間 (F) がかかる場合、単純にそれをランダムに一時停止すると、F はその行為でそれをキャッチする確率になります。それを数回行うと、コードの正確な行に至るまで、なぜそれを行っているのかが正確にわかります。

それについてもっと。 これが例です。

それを修正してから、全体的な測定を行って、節約した量を確認します.F.すすぎと繰り返し.

于 2011-05-17T12:41:25.367 に答える
1

関数の期間のチェックを処理するために作成したいくつかの手順を次に示します。私はそれらを呼び出したユニットに貼り付けてからuTesting、テスト中に uses 句に投入しました。

宣言

  Procedure TST_StartTiming(Index : Integer = 1);
    //Starts the timer by storing now in Time
    //Index is the index of the timer to use. 100 are available

  Procedure TST_StopTiming(Index : Integer = 1;Display : Boolean = True; DisplaySM : Boolean = False);
    //Stops the timer and stores the difference between time and now into time
    //Displays the result if Display is true
    //Index is the index of the timer to use. 100 are available

  Procedure TST_ShowTime(Index : Integer = 1;Detail : Boolean = True; DisplaySM : Boolean = False);
    //In a ShowMessage displays time
    //Uses DateTimeToStr if Detail is false else it breaks it down (H,M,S,MS)
    //Index is the index of the timer to use. 100 are available

宣言された変数

var
  Time : array[1..100] of TDateTime;

実装

  Procedure TST_StartTiming(Index : Integer = 1);
  begin
    Time[Index] := Now;
  end; 

  Procedure TST_StopTiming(Index : Integer = 1;Display : Boolean = True; DisplaySM : Boolean = False);
  begin
    Time[Index] := Now - Time[Index];
    if Display then TST_ShowTime;
  end;

  Procedure TST_ShowTime(Index : Integer = 1;Detail : Boolean = True; DisplaySM : Boolean = False);
  var
    H,M,S,MS : Word;
  begin
    if Detail then
      begin
        DecodeTime(Time[Index],H,M,S,MS);
        if DisplaySM then
        ShowMessage('Hour   =   ' + FloatToStr(H)  + #13#10 +
                    'Min     =   ' + FloatToStr(M)  + #13#10 +
                    'Sec      =   ' + FloatToStr(S)  + #13#10 +
                    'MS      =   ' + FloatToStr(MS) + #13#10)
        else
        OutputDebugString(PChar('Hour   =   ' + FloatToStr(H)  + #13#10 +
                    'Min     =   ' + FloatToStr(M)  + #13#10 +
                    'Sec      =   ' + FloatToStr(S)  + #13#10 +
                    'MS      =   ' + FloatToStr(MS) + #13#10));
      end
    else
      ShowMessage(TimeToStr(Time[Index]));
      OutputDebugString(Pchar(TimeToStr(Time[Index])));
  end;
于 2011-05-17T12:19:19.923 に答える