How to time a block of code
These notes cover two different techniques for timing a block of code using Delphi.
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.
Probably the easiest way of timing a block of code is to call the
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
var startTime, endTime, elapsedTime: TDateTime; begin startTime := Now(); FunctionToTime(); endTime := Now(); elapsedTime := endTime - startTime;
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.
A more accurate means of timing code is to use the high resolution timers provided by Windows - QueryPerformanceCounter and QueryPerformanceFrequency.
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
lpPerformanceCount Int64) to obtain the current counter value.
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.
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.