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: Brian Cryer 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.