确定处理例程所需的时间(例如函数的过程)的最佳和最准确的方法是什么?
我问,因为我目前正在尝试优化我的应用程序中的一些函数,当我测试更改时,如果有任何改进,只需通过查看就很难确定。因此,如果我能够返回一个准确或接近准确的处理例程的时间,那么我可以更清楚地了解代码是否有任何变化。
我考虑过使用GetTickCount,但我不确定这是否接近准确?
有一个可恢复的函数/程序来计算例程的时间是有用的,并使用它像这样:
// < prepare for calcuation of code
...
ExecuteSomeCode; // < code to test
...
// < stop calcuating code and return time it took to process
我期待听到一些建议。
感谢。
克雷格。
答案 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周期数感兴趣,而不是挂钟时间。
使用处理器时钟
此计时器非常准确。在具有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>