Cry about...
Delphi Programming


How to time a block of code


These notes cover two different techniques for timing a block of code using Delphi.

Contents

Why time?

Its a common requirement to try to make code run faster, and this is when we start optimising and tweaking our code. Unfortunately it is very easy to spend time optimising code without any real idea of where most of the processing time is being spent - and thus which bits of code ought to be optimised (and which aren't worth spending the time on).

This is a common reason for timing code. It provides a means of determining how much time a given block of code is taking to execute, and should we start optimising that code it gives us an indication of how effective our optimisations are.

Now() - for coarse timing

Probably the easiest way of timing a block of code is to call the function Now() before entering the code block and calling it again on exit from the code block. The different is the length of time the code took to execute.

For example:

var
  startTime, endTime, elapsedTime: TDateTime;
begin
  startTime := Now();
  FunctionToTime();
  endTime := Now();
  elapsedTime := endTime - startTime;

Using Now() is very simple (as this example demonstrates) but suffers because it is not very precise. Historically the internal clock was updated about every 55ms, which gives a maximum precision of 55ms. This is fine for timing code that takes a long time, but not for code where you need to know the number of milliseconds.

Newer versions of Windows, possibly depending on system hardware, may obtain the time differently, so on some systems there is the potential that the accuracy may approach 1ms.

QueryPerformanceCounter() - for accurate timing

A more accurate means of timing code is to use the high resolution timers provided by Windows - QueryPerformanceCounter and QueryPerformanceFrequency.

The function QueryPerformanceFrequency(var lpFrequency Int64) returns 0 if the system does not support a high resolution timer. If the system does support a high resolution timer (and I've yet to use this on a system that didn't) then the lpFrequency argument will be updated to hold the frequency of the timer. This is the number of "ticks" per seconds, or put another way it is the amount that the counter will increment in a second. Use QueryPerformanceCounter(var lpPerformanceCount Int64) to obtain the current counter value.

For example:

var
  startTime64, endTime64, frequency64: Int64;
  elapsedSeconds: single;
begin
  QueryPerformanceFrequency(frequency64);
  QueryPerformanceCounter(startTime64);
  FunctionToTime();
  QueryPerformanceCounter(endTime64);
  elapsedSeconds := (endTime64 - startTime64) / frequency64;

One thing to remember (and this affects the use of Now for timing too) is that there are always other processes running on your PC and because of this timings may vary from one run to the next.

Given that QueryPerformanceCounter is so easy to use, and its much greater accuracy, its a much better choice when timing code.


These notes are believed to be correct for Delphi 6 and Delphi 7 on Windows XP and may apply to other versions as well.



About the author: is a dedicated software developer and webmaster. For his day job he develops websites and desktop applications as well as providing IT services. He moonlights as a technical author and consultant.