计算例程的速度?

发布于 2024-11-07 13:59:00 字数 467 浏览 5 评论 0原文

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

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

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

如果有一个可重复使用的函数/过程来计算例程的时间,并像这样使用它会很有用:

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

我期待听到一些建议。

谢谢。

克雷格.

What would be the best and most accurate way to determine how long it took to process a routine, such as a procedure of function?

I ask because I am currently trying to optimize a few functions in my Application, when i test the changes it is hard to determine just by looking at it if there was any improvements at all. So if I could return an accurate or near accurate time it took to process a routine, I then have a more clear idea of how well, if any changes to the code have been made.

I considered using GetTickCount, but I am unsure if this would be anything near accurate?

It would be useful to have a resuable function/procedure to calculate the time of a routine, and use it something like this:

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

I look forward to hearing some suggestions.

Thanks.

Craig.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

够钟 2024-11-14 13:59:00

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

From my knowledge, the most accurate method is by using QueryPerformanceFrequency:

code:

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; 
韵柒 2024-11-14 13:59:00

尝试 Eric Grange 的采样分析器

Try Eric Grange's Sampling Profiler.

浪漫人生路 2024-11-14 13:59:00

从 Delphi 6 开始,您可以使用 x86 时间戳计数器。
这对 CPU 周期进行计数,在 1 Ghz 处理器上,每次计数需要一纳秒。
没有比这更准确的了。

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()
如果 CPU 速度减慢,QueryPerformanceCounter() 会提供相同的时间,从而补偿 CPU 限制。如果您的 CPU 由于过热或其他原因而变慢,RDTSC 将为您提供相同数量的周期。
因此,如果您的 CPU 开始运行过热并需要降低速度,QueryPerformanceCounter() 会说您的例程花费了更多时间(这是误导性的),而 RDTSC 会说它需要相同数量的周期(这是准确的)
这就是您想要的,因为您感兴趣的是代码使用的 CPU 周期数,而不是挂钟时间。

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

使用处理器时钟

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

何时不使用 RDTSC
RDTSC 对于基本计时很有用。如果您要在单 CPU 计算机上对多线程代码进行计时,RDTSC 将正常工作。如果您有多个 CPU,则起始计数可能来自一个 CPU,而结束计数可能来自另一个 CPU。
因此,不要使用 RDTSC 在多 CPU 计算机上对多线程代码进行计时。在单 CPU 机器上它工作得很好,或者在多 CPU 机器上单线程代码也很好。
另请记住,RDTSC 会计算 CPU 周期。如果有一些需要时间但不使用 CPU 的东西,比如磁盘 IO 或网络,那么 RDTSC 就不是一个好的工具。

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

From Delphi 6 upwards you can use the x86 Timestamp counter.
This counts CPU cycles, on a 1 Ghz processor, each count takes one nanosecond.
Can't get more accurate than that.

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;

On x64 the following code is more accurate, because it does not suffer from the delay of 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

Use the above code to get the timestamp before and after executing your code.
Most accurate method possible and easy as pie.

Note that you need to run a test at least 10 times to get a good result, on the first pass the cache will be cold, and random harddisk reads and interrupts can throw off your timings.
Because this thing is so accurate it can give you the wrong idea if you only time the first run.

Why you should not use QueryPerformanceCounter()
QueryPerformanceCounter() gives the same amount of time if the CPU slows down, it compensates for CPU thottling. Whilst RDTSC will give you the same amount of cycles if your CPU slows down due to overheating or whatnot.
So if your CPU starts running hot and needs to throttle down, QueryPerformanceCounter() will say that your routine is taking more time (which is misleading) and RDTSC will say that it takes the same amount of cycles (which is accurate).
This is what you want because you're interested in the amount of CPU-cycles your code uses, not the wall-clock time.

From the lastest intel docs: http://software.intel.com/en-us/articles/measure-code-sections-using-the-enhanced-timer/?wapkw=%28rdtsc%29

Using the Processor Clocks

This timer is very accurate. On a system with a 3GHz processor, this timer can measure events that last less than one nanosecond. [...] If the frequency changes while the targeted code is running, the final reading will be redundant since the initial and final readings were not taken using the same clock frequency. The number of clock ticks that occurred during this time will be accurate, but the elapsed time will be an unknown.

When not to use RDTSC
RDTSC is useful for basic timing. If you're timing multithreaded code on a single CPU machine, RDTSC will work fine. If you have multiple CPU's the startcount may come from one CPU and the endcount from another.
So don't use RDTSC to time multithreaded code on a multi-CPU machine. On a single CPU machine it works fine, or single threaded code on a multi-CPU machine it is also fine.
Also remember that RDTSC counts CPU cycles. If there is something that takes time but doesn't use the CPU, like disk-IO or network than RDTSC is not a good tool.

But the documentation says RDTSC is not accurate on modern CPU's
RDTSC is not a tool for keeping track of time, it's a tool for keeping track of CPU-cycles.
For that it is the only tool that is accurate. Routines that keep track of time are not accurate on modern CPU's because the CPU-clock is not absolute like it used to be.

情话墙 2024-11-14 13:59:00

您没有指定您的 Delphi 版本,但 Delphi XE 在单元诊断中声明了一个 TStopWatch。这将使您能够以合理的精度测量运行时间。

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

You didn't specify your Delphi version, but Delphi XE has a TStopWatch declared in unit Diagnostics. This will allow you to measure the runtime with reasonable precision.

uses
  Diagnostics;
var
  sw: TStopWatch;
begin
  sw := TStopWatch.StartNew;
  <dosomething>
  Writeln(Format('runtime: %d ms', [sw.ElapsedMilliseconds]));
end;
故笙诉离歌 2024-11-14 13:59:00

我问是因为我目前正在尝试
优化一些功能

人们很自然地认为测量是找出要优化的内容的方法,但还有更好的方法。

如果某件事需要足够长的时间(F)来值得优化,那么如果您只是随机暂停它,F 就是您在行为中捕获它的概率。
这样做几次,你就会明白为什么要这么做,甚至到具体的代码行。

更多信息。
这是一个示例。

修复它,然后进行整体测量一下你节省了多少钱,应该是 F 左右。
冲洗并重复。

I ask because I am currently trying to
optimize a few functions

It is natural to think that measuring is how you find out what to optimize, but there's a better way.

If something takes a large enough fraction of time (F) to be worth optimizing, then if you simply pause it at random, F is the probability you will catch it in the act.
Do that several times, and you will see precisely why it's doing it, down to the exact lines of code.

More on that.
Here's an example.

Fix it, and then do an overall measurement to see how much you saved, which should be about F.
Rinse and repeat.

∝单色的世界 2024-11-14 13:59:00

以下是我为处理检查函数的持续时间而编写的一些程序。我将它们放在一个名为 uTesting 的单元中,然后在测试期间将其放入 use 子句中。

声明

  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;

Here are some procedures I made to handle checking the duration of a function. I stuck them in a unit I called uTesting and then just throw into the uses clause during my testing.

Declaration

  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

variables declared

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

Implementation

  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;
帝王念 2024-11-14 13:59:00

clock_gettime() 是高级解决方案,精确到纳秒,您还可以使用 rtdsc,精确到 CPU 周期,最后您可以简单地使用 gettimeofday()

clock_gettime() is the high solution, which is precise to nano seconds, you can also use rtdsc, which is precise to CPU cycle, and lastly you can simply use gettimeofday().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文