Delphi 为什么QueryperformanceCounter的计时与挂钟不同?

Delphi 为什么QueryperformanceCounter的计时与挂钟不同?,delphi,performancecounter,Delphi,Performancecounter,您好,我正在使用QueryperformanceCounter对Delphi中的代码块计时。由于某种原因,这个 我用QueryPerformanceCounter得到的毫秒数和用秒表得到的挂钟时间大不相同。例如,秒表给了我大约33秒的时间,如果不准确的话,这似乎是正确的,但是使用QueryProFomanceCounter会给我一个大约500毫秒的数字 当我浏览代码时,我可以看到QueryPerformanceFrequency为我的CPU提供了正确的CPU频率,为Core2 E6600提供了2

您好,我正在使用QueryperformanceCounter对Delphi中的代码块计时。由于某种原因,这个 我用QueryPerformanceCounter得到的毫秒数和用秒表得到的挂钟时间大不相同。例如,秒表给了我大约33秒的时间,如果不准确的话,这似乎是正确的,但是使用QueryProFomanceCounter会给我一个大约500毫秒的数字

当我浏览代码时,我可以看到QueryPerformanceFrequency为我的CPU提供了正确的CPU频率,为Core2 E6600提供了2.4G。因此,如果勾号正确,
(勾号/Freq)*1000
应该为我正在计时的代码提供正确的执行时间,但为什么不呢

我知道,对于我正在尝试计时的代码,QeuryPerformanceCounter可能是杀伤力过大,因为它花费的时间是秒而不是百万秒,但我更感兴趣的是理解wall clock和QueryPerManceCounter之间时间差的原因

我的硬件是E6600 Core2,如果相关的话,操作系统是Windows7 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.

您应该发布一段代码片段来演示问题……但我假设您有一个错误:

Milliseconds := 1000 * ((StopCount - StartCount) / Frequency);
如果您要与秒表进行比较,您可能会选择更简单的路线,只需捕获之前和之后的TDateTime(使用Now()),然后使用DateUtils毫秒span()方法计算差异:

var
  MyStartDate:TDateTime;
  MyStopDate:TDateTime;
  MyTiming:Double;
begin
  MyStartDate := Now();
  DoSomethingYouWantTimed();
  MyStopDate := Now();
  MyTiming := MilliSecondSpan(MyStopDate, MyStartDate);
  DoSomethingWithTiming(MyTiming);
end;

这段代码适合我,也许你可以试试:

  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.毫秒,则“睡眠(10)”的持续时间可以为0毫秒


注2:不要长时间依赖QueryPerformanceCounter,因为它的时间在一天内会慢慢消失(大约每分钟1毫秒的差异)

我使用NTP服务器定期同步PC时钟,PC时钟会持续很长时间以调整QueryPerformanceCounter的“滴答”时间,以及用于精确时间测量的校准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;
函数NowInternal:TDateTime;
常数
//同步高分辨率时钟之间的最长时间(秒)
最大同步时间=10;
变量
LPPerformanceCount:Int64;
lResult:TDateTime;
LdateTimeSynchronized:布尔值;
开始
//检查高分辨率性能计数器频率是否正确
//草签
fDateTimeCritSect.Enter;
尝试
如果(FPPerformanceFrequency<0)和
不查询性能频率(FPPerformanceFrequency)则
FPPerformanceFrequency:=0;
如果FPPerformanceFrequency>0,则开始
//从高分辨率性能计数器获取返回值
如果(fWindowsStartTime CSI_NULL_DATE_TIME)和
QueryPerformanceCounter(LPPerformanceCount)然后
lResult:=fWindowsStartTime+
L性能计数/F性能频率/秒日
其他的
lResult:=CSI\u NULL\u日期\u时间;
如果(lResult和Now之间的毫秒数)>=MAX\u CLOCK\u DIFF)或
(秒之间(现在,fLastSyncTime)>=最大同步时间)然后开始
//由于时钟差异或错误,请重新同步高分辨率时钟
//至少每10秒
LdateTimeSynchronized:=SyncDateTime;
//从高分辨率性能计数器获取返回值
如果(fWindowsStartTime CSI_NULL_DATE_TIME)和
QueryPerformanceCounter(LPPerformanceCount)然后
lResult:=fWindowsStartTime+
L性能计数/F性能频率/秒日;
结束其他
LdateTimeSynchronized:=假;
如果(lResult,Now)>=(最大时钟差*2)之间的毫秒数,则
//如果需要,将返回值默认为标准低分辨率值
//有什么不对劲吗
结果:=现在
其他的
结果:=lResult;
结束,否则开始
LdateTimeSynchronized:=假;
//默认返回值为标准低分辨率值,因为
//我们不能使用高分辨率的时钟
结果:=现在;
结束;
最后
FDATETIMECRIT.离开;
结束;
如果LDATETIMESynchronized,则
CsiGlobals.AddLogMsg(“高分辨率时钟同步”,CSI\u LC\u时钟);
结束;
函数SyncDateTime:布尔值;
变量
第一类:红衣主教;
优先级:整数;
lInitTime:TDateTime;
lNextTime:TDateTime;
LPPerformanceCount:Int64;
lHighResCurrentTime:TDateTime;
lLowResCurrentTime:TDateTime;
开始
//同步高分辨率的日期/时间结构(增强线程
//同步过程中优先级尽可能高)
lPriorityClass:=CsiGetProcessPriorityClass;
lThreadPriority:=CsiGetCurrentThreadPriority;
尝试
CsiSetProcessPriorityClass(实时优先级类);
CsiSetCurrentThreadPriority(线程优先级时间关键);
//循环,直到低分辨率日期/时间值更改(这将加载
//CPU,但最多只能使用约15毫秒)
lInitTime:=现在;
lNextTime:=现在;
而lNextTime=linttime do
lNextTime:=现在;
//调整高分辨率性能计数器的时钟漂移频率
如果(fWindowsStartTime CSI_NULL_DATE_TIME)和
QueryPerformanceCounter(LPPerformanceCount)然后开始
lHighResCurrentTime:=fWindowsStartTime+
L性能计数/F性能频率/
第二天;
lLowResCurrentTime:=现在;
如果之间存在毫秒(lHighResCurrentTime、lLowResCurrentTime)<
(最大时钟差*2)然后
FPPerformanceFrequency:=四舍五入((1+