1. Home
  2. Computing & Technology
  3. Delphi Programming

How to Accurately Measure Elapsed Time Using High-Resolution Performance Counter
The TStopWatch Delphi Class Implements a Very Accurate Process Execution Timer

By Zarko Gajic, About.com

tstopwatch delphi high performance timer

tstopwatch delphi high performance timer

In most "normal" database applications, for example, it is not that much important if some piece of code would take 1 or 2 seconds to execute.

What about when you need to process millions of tree leafs or generate zillions of unique random numbers? In such scenarios it is important that your code executes as much fast as possible.

Timing Out Your Code

In some applications, a very accurate, high-precision time measurement methods are important.

Using RTL's Now
The first idea that comes to (my) mind is simply using the Now function. Now, defined in the SysUtils unit, returns the current system date and time.

A few lines of code can be used to measure elapsed time between "start" and "stop" of some process:

var
  start, stop, elapsed : TDateTime;
begin
  start := Now;
  //TimeOutThis();
  stop := Now;
  elapsed := stop - start;
end;
Now function returns the current system date and time that is accurate up to 10 milliseconds (Windows NT and later) or 55 milliseconds (Windows 98).

For very small intervals the precision of "Now" is sometimes not enough.

Using Windows API GetTickCount
The second idea that comes to mind is using the GetTickCount Windows API function. GetTickCount retrieves the number of milliseconds that have elapsed since the system was started, but the function only has the precision of 1 ms and is not very accurate.

The elapsed time is stored as an int64 value. Therefore, the time will wrap around to zero if Windows is run continuously for 49.7 days.

var
  start, stop, elapsed : cardinal;
begin
  start := GetTickCount;
  //TimeOutThis();
  stop := GetTickCount;
  elapsed := stop - start; //milliseconds
end;

GetTickCount is also limited to the accuracy of the system timer (10 / 55 ms).

High Precision Timing Out Your Code

Finally, if a high-resolution performance counter exists on the system, you can use the QueryPerformanceFrequency Windows API function to express the frequency, in counts per second. The value of the count is processor dependent.

QueryPerformanceFrequency returns the number of "ticks" per seconds - it is the amount that the counter, QueryPerformanceCounter, will increment in a second.

The QueryPerformanceCounter function retrieves the current value of the high-resolution performance counter. By calling this function at the beginning and end of a section of code, an application uses the counter as a high-resolution timer.

The accuracy of a high-resolution timers is around few hundred nanoseconds. A nanosecond is a unit of time representing 0.000000001 or 1 billionth of a second!

TStopWatch - Delphi Implementation Of a High Resolution Counter

Here's the implementation of a custom high-resolution timer in Delphi: TStopWatch. I've borrowed the name from .Net :)

TStopWatch measures elapsed time by counting timer ticks in the underlying timer mechanism.

  • The IsHighResolution property indicates whether the timer is based on a high-resolution performance counter.
  • The Start method starts measuring elapsed time.
  • The Stop method stops measuring elapsed time.
  • The ElapsedMilliseconds property gets the total elapsed time in milliseconds.
  • The Elapsed property gets the total elapsed time in timer ticks.
unit StopWatch;

interface

uses Windows, SysUtils, DateUtils;

type TStopWatch = class
  private
    fFrequency : TLargeInteger;
    fIsRunning: boolean;
    fIsHighResolution: boolean;
    fStartCount, fStopCount : TLargeInteger;
    procedure SetTickStamp(var lInt : TLargeInteger) ;
    function GetElapsedTicks: TLargeInteger;
    function GetElapsedMiliseconds: TLargeInteger;
    function GetElapsed: string;
  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 Elapsed : string read GetElapsed;
    property IsRunning : boolean read fIsRunning;
  end;

implementation

constructor TStopWatch.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 TStopWatch.GetElapsedTicks: TLargeInteger;
begin
  result := fStopCount - fStartCount;
end;

procedure TStopWatch.SetTickStamp(var lInt : TLargeInteger) ;
begin
  if fIsHighResolution then
    QueryPerformanceCounter(lInt)
  else
    lInt := MilliSecondOf(Now) ;
end;

function TStopWatch.GetElapsed: string;
var
  dt : TDateTime;
begin
  dt := ElapsedMiliseconds / MSecsPerSec / SecsPerDay;
  result := Format('%d days, %s', [Trunc(dt), FormatDateTime('hh:nn:ss.z', Frac(dt))]) ;
end;

function TStopWatch.GetElapsedMiliseconds: TLargeInteger;
begin
  result := (MSecsPerSec * (fStopCount - fStartCount)) div fFrequency;
end;

procedure TStopWatch.Start;
begin
  SetTickStamp(fStartCount) ;
  fIsRunning := true;
end;

procedure TStopWatch.Stop;
begin
  SetTickStamp(fStopCount) ;
  fIsRunning := false;
end;
end.
Here's an example of usage:
var
  sw : TStopWatch;
  elapsedMiliseconds : cardinal;
begin
  sw := TStopWatch.Create() ;
  try
    sw.Start;
    //TimeOutThisFunction()
    sw.Stop;

    elapsedMiliseconds := sw.ElapsedMiliseconds;
  finally
    sw.Free;
  end;
end;
Zarko Gajic
Guide since 1998

Zarko Gajic
Delphi Programming Guide

Explore Delphi Programming
About.com Special Features

Stay connected and entertained with reviews on tips on the latest HDTVs, cellphones and more. More >

Easy ways to connect two computers for networking purposes. More >

  1. Home
  2. Computing & Technology
  3. Delphi Programming
  4. Advanced Delphi Techniques
  5. High Performance Timer in Delphi - TStopWatch - High-Resolution Counter

©2009 About.com, a part of The New York Times Company.

All rights reserved.