您好我正在使用QueryperformanceCounter来计算Delphi中的代码块。出于某种原因, 使用QueryPerformanceCounter获得的毫秒数与使用秒表的挂钟时间完全不同。例如,秒表给了我大约33秒,如果不准确,这似乎是正确的,但使用QueryPerofomanceCounter将给我一个像500毫秒的数字。
通过我的代码后,我可以看到QueryPerformanceFrequency为我的CPU提供了正确的CPU频率,为Core2 E6600提供了2.4G的CPU频率。因此,如果勾号是正确的,(tick number / Freq) * 1000
应该给我正确的代码执行时间,但为什么不呢?
我知道对于我正在尝试计时的代码,QeuryPerformanceCounter可能过度杀死,因为花了几秒而不是百万秒,但我更感兴趣的是了解挂钟和QueryPerormanceCounter之间时差的原因。
我的硬件是E6600 Core2,如果相关,操作系统是Windows 7 X64。
unit PerformanceTimer;
interface
uses Windows, SysUtils, DateUtils;
type TPerformanceTimer = class
private
fFrequency : TLargeInteger;
fIsRunning: boolean;
fIsHighResolution: boolean;
fStartCount, FstopCount : TLargeInteger;
procedure SetTickStamp(var lInt : TLargeInteger) ;
function GetElapsedTicks: TLargeInteger;
function GetElapsedMiliseconds: TLargeInteger;
public
constructor Create(const startOnCreate : boolean = false) ;
procedure Start;
procedure Stop;
property IsHighResolution : boolean read fIsHighResolution;
property ElapsedTicks : TLargeInteger read GetElapsedTicks;
property ElapsedMiliseconds : TLargeInteger read GetElapsedMiliseconds;
property IsRunning : boolean read fIsRunning;
end;
implementation
constructor TPerformanceTimer.Create(const startOnCreate : boolean = false) ;
begin
inherited Create;
fIsRunning := false;
fIsHighResolution := QueryPerformanceFrequency(fFrequency) ;
if NOT fIsHighResolution then
fFrequency := MSecsPerSec;
if startOnCreate then
Start;
end;
function TPerformanceTimer.GetElapsedTicks: TLargeInteger;
begin
result := fStopCount - fStartCount;
end;
procedure TPerformanceTimer.SetTickStamp(var lInt : TLargeInteger) ;
begin
if fIsHighResolution then
QueryPerformanceCounter(lInt)
else
lInt := MilliSecondOf(Now) ;
end;
function TPerformanceTimer.GetElapsedMiliseconds: TLargeInteger;
begin
result := (MSecsPerSec * (fStopCount - fStartCount)) div fFrequency;
end;
procedure TPerformanceTimer.Start;
begin
SetTickStamp(fStartCount) ;
fIsRunning := true;
end;
procedure TPerformanceTimer.Stop;
begin
SetTickStamp(fStopCount) ;
fIsRunning := false;
end;
end.
答案 0 :(得分:4)
这段代码对我有用,也许你可以尝试一下:
var
ifrequency, icount1, icount2: Int64;
fmsec: Double;
begin
QueryPerformanceFrequency(ifrequency);
QueryPerformanceCounter(icount1);
Sleep(500);
QueryPerformanceCounter(icount2);
fmsec := 1000 * ((icount2 - icount1) / ifrequency);
end;
fmsec约为499.6或类似的东西。
注意:不要依赖Now或TickCount来获取小数字:它们的间隔大约为10毫秒(取决于Windows版本)!因此,如果你使用Now和DateUtils.MillisecondsBetween
,“sleep(10)”的持续时间可以给0ms注意2:不要长时间依赖QueryPerformanceCounter,因为它的时间可以在一天内慢慢消失(每分钟大约1ms差异)
答案 1 :(得分:3)
如果您的硬件支持动态频率缩放,则意味着QueryPerformanceFrequency不能返回持续描述动态变化的静态值。每当计算机激进的东西开始时,适应CPU速度将阻止精确测量。
至少,我的笔记本电脑经验丰富 - 因为它改为更高的时钟频率,基于QueryPerformanceCounter的测量结果搞砸了。
因此,无论提供更高的准确度,我仍然大部分时间都使用GetTickCount用于此类目的(但是如前所述,基于DateTime的测量也没问题,除非可能发生时区切换),有些“温暖 - up“开始耗尽CPU功率的代码片段,因此当相关代码片段开始执行时,CPU速度达到其(常数)最大值。
答案 2 :(得分:2)
您应该发布一个代码片段来演示问题...但我会假设您出错:
Milliseconds := 1000 * ((StopCount - StartCount) / Frequency);
如果您要与秒表进行比较,您可以采用更简单的路线,只需捕捉前后的TDateTime(使用 Now()),然后使用DateUtils MilliSecondSpan ()计算差异的方法:
var
MyStartDate:TDateTime;
MyStopDate:TDateTime;
MyTiming:Double;
begin
MyStartDate := Now();
DoSomethingYouWantTimed();
MyStopDate := Now();
MyTiming := MilliSecondSpan(MyStopDate, MyStartDate);
DoSomethingWithTiming(MyTiming);
end;
答案 3 :(得分:0)
我使用NTP服务器定期同步PC时钟,PC时钟在很长一段时间内调整QueryPerformanceCounter“tick”时间,并使用校准后的QueryPerformanceCounter时间进行精确时间测量。在时钟漂移较低的良好服务器上,这意味着我在一段时间内具有精确度,远小于一毫秒,并且所有机器的时钟时间都在一毫秒或两秒内同步。下面附有一些相关代码:
function NowInternal: TDateTime;
const
// maximum time in seconds between synchronising the high-resolution clock
MAX_SYNC_TIME = 10;
var
lPerformanceCount: Int64;
lResult: TDateTime;
lDateTimeSynchronised: Boolean;
begin
// check that the the high-resolution performance counter frequency has been
// initialised
fDateTimeCritSect.Enter;
try
if (fPerformanceFrequency < 0) and
not QueryPerformanceFrequency(fPerformanceFrequency) then
fPerformanceFrequency := 0;
if fPerformanceFrequency > 0 then begin
// get the return value from the the high-resolution performance counter
if (fWindowsStartTime <> CSI_NULL_DATE_TIME) and
QueryPerformanceCounter(lPerformanceCount) then
lResult := fWindowsStartTime +
lPerformanceCount / fPerformanceFrequency / SecsPerDay
else
lResult := CSI_NULL_DATE_TIME;
if (MilliSecondsBetween(lResult, Now) >= MAX_CLOCK_DIFF) or
(SecondsBetween(Now, fLastSyncTime) >= MAX_SYNC_TIME) then begin
// resynchronise the high-resolution clock due to clock differences or
// at least every 10 seconds
lDateTimeSynchronised := SyncDateTime;
// get the return value from the the high-resolution performance counter
if (fWindowsStartTime <> CSI_NULL_DATE_TIME) and
QueryPerformanceCounter(lPerformanceCount) then
lResult := fWindowsStartTime +
lPerformanceCount / fPerformanceFrequency / SecsPerDay;
end else
lDateTimeSynchronised := False;
if MilliSecondsBetween(lResult, Now) >= (MAX_CLOCK_DIFF * 2) then
// default the return value to the standard low-resolution value if
// anything has gone wrong
Result := Now
else
Result := lResult;
end else begin
lDateTimeSynchronised := False;
// default the return value to the standard low-resolution value because
// we cannot use the high-resolution clock
Result := Now;
end;
finally
fDateTimeCritSect.Leave;
end;
if lDateTimeSynchronised then
CsiGlobals.AddLogMsg('High-resolution clock synchronised', CSI_LC_CLOCK);
end;
function SyncDateTime: Boolean;
var
lPriorityClass: Cardinal;
lThreadPriority: Integer;
lInitTime: TDateTime;
lNextTime: TDateTime;
lPerformanceCount: Int64;
lHighResCurrentTime: TDateTime;
lLowResCurrentTime: TDateTime;
begin
// synchronise the high-resolution date/time structure (boost the thread
// priority as high as possible during synchronisation)
lPriorityClass := CsiGetProcessPriorityClass;
lThreadPriority := CsiGetCurrentThreadPriority;
try
CsiSetProcessPriorityClass(REALTIME_PRIORITY_CLASS);
CsiSetCurrentThreadPriority(THREAD_PRIORITY_TIME_CRITICAL);
// loop until the low-resolution date/time value changes (this will load the
// CPU, but only for a maximum of around 15 milliseconds)
lInitTime := Now;
lNextTime := Now;
while lNextTime = lInitTime do
lNextTime := Now;
// adjust the high-resolution performance counter frequency for clock drift
if (fWindowsStartTime <> CSI_NULL_DATE_TIME) and
QueryPerformanceCounter(lPerformanceCount) then begin
lHighResCurrentTime := fWindowsStartTime +
lPerformanceCount / fPerformanceFrequency /
SecsPerDay;
lLowResCurrentTime := Now;
if MilliSecondsBetween(lHighResCurrentTime, lLowResCurrentTime) <
(MAX_CLOCK_DIFF * 2) then
fPerformanceFrequency := Round((1 +
(lHighResCurrentTime -
lLowResCurrentTime) /
(lLowResCurrentTime - fLastSyncTime)) *
fPerformanceFrequency);
end;
// save the Windows start time by extrapolating the high-resolution
// performance counter value back to zero
if QueryPerformanceCounter(lPerformanceCount) then begin
fWindowsStartTime := lNextTime -
lPerformanceCount / fPerformanceFrequency /
SecsPerDay;
fLastSyncTime := Now;
Result := True;
end else
Result := False;
finally
CsiSetCurrentThreadPriority(lThreadPriority);
CsiSetProcessPriorityClass(lPriorityClass);
end;
end;