计算程序的速度?

时间:2011-05-17 12:12:30

标签: performance delphi optimization gettickcount

确定处理例程所需的时间(例如函数的过程)的最佳和最准确的方法是什么?

我问,因为我目前正在尝试优化我的应用程序中的一些函数,当我测试更改时,如果有任何改进,只需通过查看就很难确定。因此,如果我能够返回一个准确或接近准确的处理例程的时间,那么我可以更清楚地了解代码是否有任何变化。

我考虑过使用GetTickCount,但我不确定这是否接近准确?

有一个可恢复的函数/程序来计算例程的时间是有用的,并使用它像这样:

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

我期待听到一些建议。

感谢。

克雷格。

8 个答案:

答案 0 :(得分:23)

据我所知,最准确的方法是使用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; 

答案 1 :(得分:18)

尝试Eric Grange的Sampling Profiler

答案 2 :(得分:13)

从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 thottling。如果你的CPU由于过热或其他因素而减速,RDTSC会给你相同的周期数 因此,如果您的CPU开始变热并且需要减速,QueryPerformanceCounter()会说您的例程花费更多时间(这是误导性的)并且RDTSC会说它需要相同的量周期(这是准确的) 这就是你想要的,因为你对代码使用的CPU周期数感兴趣,而不是挂钟时间。

来自最新的英特尔文档:http://software.intel.com/en-us/articles/measure-code-sections-using-the-enhanced-timer/?wapkw=%28rdtsc%29

  

使用处理器时钟

     

此计时器非常准确。在具有3GHz处理器的系统上,此计时器可以测量持续时间小于1纳秒的事件。 [...]如果在目标代码运行时频率发生变化,则最终读数将是多余的,因为不使用相同的时钟频率进行初始和最终读数。 在此期间发生的时钟滴答数将是准确的,但经过的时间将是未知的。

何时不使用RDTSC
RDTSC对基本时序很有用。如果您在单个CPU机器上计时多线程代码,RDTSC将正常工作。如果您有多个CPU,则startcount可能来自一个CPU,而endcount来自另一个CPU 所以不要使用RDTSC来计算多CPU机器上的多线程代码。在单CPU机器上它可以正常工作,或者在多CPU机器上使用单线程代码也很好 还记得RDTSC计算CPU周期。如果有一些需要时间但不使用CPU的东西,比如磁盘IO或网络而不是RDTSC不是一个好工具。

但是文档说RDTSC在现代CPU上不准确
RDTSC 是一种跟踪时间的工具,它是一种跟踪CPU周期的工具。
为此,唯一工具是准确的。跟踪时间的例程在现代CPU上并不准确,因为CPU时钟并不像以前那样绝对。

答案 3 :(得分:10)

您未指定Delphi版本,但Delphi XE在单元诊断中声明了TStopWatch。这将允许您以合理的精度测量运行时间。

uses
  Diagnostics;
var
  sw: TStopWatch;
begin
  sw := TStopWatch.StartNew;
  <dosomething>
  Writeln(Format('runtime: %d ms', [sw.ElapsedMilliseconds]));
end;

答案 4 :(得分:6)

  

我问,因为我正在努力   优化一些功能

很自然地认为测量是你如何找到优化的方法,但有一种更好的方法。

如果某些事情需要花费足够长的时间(F)来进行优化,那么如果您只是随意暂停,F就是您在行动中捕获它的概率。 这样做几次,你会看到它正在做的原因,直到确切的代码行。

More on that. Here's an example.

修复它,然后进行整体测量以查看您节省了多少,这应该是关于F. 冲洗并重复。

答案 5 :(得分: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;

答案 6 :(得分:0)

答案 7 :(得分:0)

clock_gettime()是高解决方案,精确到纳秒,您也可以使用rtdsc,这对CPU周期来说是精确的,最后您只需使用gettimeofday()。< / p>