![]() |
![]() |
|||||
A new article has been written that describes a counter component. Look [ HERE ]. Introduction For time measurements, the Delphi programmer may use a milliseconds counter.This counter variable is of type cardinal and delivers the expired time in milliseconds since Windows was activated. (So, it overflows after 49 days). However, the accuracy is not very good, because the counter is not updated every millisecond but much less. The following little program shows the behaviour of the timer: procedure TForm1.Button1Click(Sender: TObject); var t : cardinal; begin t := gettickcount; while gettickcount = t do; label1.Caption := inttostr(gettickcount-t); end;In this case, label1 shows mostly a count of 16 and sometimes a count of 15 which indicates that the timer value is incremented only once per 15,xxx milliseconds. Also, most processes complete in less than a millisecond so, for accurate measurement, they have to be repeated hundreds of times. The Pentium processor however has a 64 bit counter that is updated every clock cycle. The RDTSC instruction (code $0F,$31) copies bits 0..31 to register EAX and bits 32..63 to register EDX. This allows for accurate measurements of short execution times. Problem however is, that the CPU clock speed is system dependable. To calculate elapsed time in microseconds, first the clock frequency of the processor must be calculated. This is accomplished by sampling the CPU's 64 bit clock register, wait 500 milliseconds using the milliseconds timer, then sampling the CPU clock counter again. The difference of the CPU clock values divided by the elapsed time in milliseconds * 10-3 makes the clock speed in MHz (megahertz). Once the clock speed is set, proces time is calculated by
- execute the process - sample the CPU clock again - calculate the difference of the clocks - convert this difference to the "double" floating point format - divide this difference by the previously set CPU clock frequency
2. the Delphi version does not support 64 bit integers Support of 64 bit integers See listing below:
unit Unit1;
{
a microseconds clock for Delphi versions
that support 64 bit integers (type = Int64)
}
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
StaticText1: TStaticText;
Button2: TButton;
StaticText2: TStaticText;
Label1: TLabel;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
CPUclock : double; //CPU clock speed in MHz
implementation
{$R *.dfm}
procedure getCPUticks(var count: Int64);
//store 64 bits CPU clock in variable count
begin
asm
mov ECX,count;
RDTSC; //lower 32 bits --> EAX, upper 32 bits ---> EDX
//RDTSC = DB $0F,$31
mov [ECX],EAX;
add ECX,4;
mov [ECX],EDX;
end;
end;
procedure setCPUclock;
//set variable CPUclock
//call this procedure before measuring any process time
var t1,t2 : cardinal; //system clock ticks
cput1,cput2 : Int64; //CPU clock ticks
begin
t1 := getTickCount; //get milliseconds clock
while getTickCount = t1 do; //sync with start of 1 millisec interval
getCPUticks(cput1); //get CPU clock count
t1 := gettickcount;
repeat
getCPUticks(cput2); //get CPU clock count
t2 := gettickcount;
until t2-t1 >= 500;
CPUclock := (cput2-cput1)/((t2-t1)*1e3); //set CPU clock in microsecs
form1.statictext2.caption := formatfloat('####',CPUclock)+' MHz';
end;
procedure TForm1.Button1Click(Sender: TObject);
//button1 enables display of CPU ticks in statictext1
var count: Int64;
begin
getCPUticks(count);
statictext1.Caption := inttostr(count);
end;
procedure TForm1.Button2Click(Sender: TObject);
//a process of which time is measured
var t1,t2 : Int64;
i : integer;
x,y : double;
begin
getCPUticks(t1);
x := 1000; //process starts here
y := 1;
for i := 1 to 1000 do
begin
x := (x + y)2;
y := 1000x;
end; //process ends here
getCPUticks(t2);
statictext1.Caption := formatfloat('####.####',(t2-t1)/CPUclock) + ' microsecs';
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
application.ProcessMessages;
setCPUclock;
end;
procedure TForm1.FormActivate(Sender: TObject);
begin
application.ProcessMessages;
setCPUclock;
end;
end.
64 bit integers not supported In this case, a variable type I64 is created.type I64 = array[0..1] of cardinal function I64ToFloat(a : I64) : double converts the new I64 format to floating point double procedure Diff64(t2,t1 : I64) calculates the difference t2 := t2 - t1 Below is the source code
unit Unit1;
{
a microseconds clock for Delphi versions
that do not support 64 bit integers
a type I64 = array[0..1] of cardinal is used instead
}
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
StaticText1: TStaticText;
Button2: TButton;
StaticText2: TStaticText;
Label1: TLabel;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
I64 = array[0..1] of cardinal; //64 bit integer
var
Form1: TForm1;
CPUclock : double; //CPU clock speed in MHz
implementation
{$R *.dfm}
procedure getCPUticks(var count: I64);
//store 64 bits CPU clock in variable count
begin
asm
mov ECX,count;
RDTSC; //lower 32 bits --> EAX, upper 32 bits ---> EDX
//RDTSC = DB $0F,$31
mov [ECX],EAX;
add ECX,4;
mov [ECX],EDX;
end;
end;
procedure diff64(var a,b : I64);
//set a to a-b
begin
asm
mov ECX,a;
mov EAX,[ECX];
mov EDX,[ECX+4];
mov ECX,b
sub EAX,[ECX]; //subtract
sbb EDX,[ECX+4]; //subtract with borrow
mov ECX,a;
mov [ECX],EAX;
mov [ECX+4],EDX;
end;
end;
function I64ToFloat(const a : I64) : double;
//convert I64 integer to floating point double
begin
result := a[0] + a[1]*4294967296;
end;
function I64ToHex(a : I64) : string;
//convert 64 bits integer to hexadecimal string
const v : array[0..15] of char =
('0','1','2','3','4','5','6','7',
'8','9','a','b','c','d','e','f');
var i,j : byte;
begin
result := '';
for i := 1 downto 0 do
for j := 7 downto 0 do
result := result + v[(a[i] shr (j*4)) and $f];
end;
procedure setCPUclock;
//set variable CPUclock
//call this procedure before measuring any process time
var t1,t2 : cardinal; //system clock ticks
cput1,cput2 : I64; //CPU clock ticks
begin
t1 := getTickCount; //get milliseconds clock
while getTickCount = t1 do; //sync with update of 1 millisec interval
getCPUticks(cput1); //get CPU clock count
t1 := gettickcount; //get millisecs clock time
repeat
getCPUticks(cput2); //get CPU clock count
t2 := gettickcount;
until t2-t1 >= 500;
diff64(cput2,cput1);
CPUclock := I64ToFloat(cput2)/((t2-t1)*1e3); //set CPU clock in microsecs
form1.statictext2.caption := formatfloat('####',CPUclock)+' MHz';
end;
procedure TForm1.Button1Click(Sender: TObject);
//button1 enables display of CPU ticks in statictext1
var count: I64;
begin
getCPUticks(count);
statictext1.Caption := I64ToHex(count);
end;
procedure TForm1.Button2Click(Sender: TObject);
//a process of which time is measured
var t1,t2 : I64;
i : integer;
x,y,z : double;
begin
getCPUticks(t1);
x := 1000; //process starts here
y := 1;
for i := 1 to 1000 do
begin
x := (x + y)2;
y := 1000x;
end; //process ends here
getCPUticks(t2);
diff64(t2,t1); //t2 := t2 - t1
z := I64ToFloat(t2);
statictext1.Caption := formatfloat('####.####',z/CPUclock) + ' microsecs';
end;
procedure TForm1.FormActivate(Sender: TObject);
//calculate CPU clock speed
begin
application.processmessages;
setCPUclock;
end;
procedure TForm1.Button3Click(Sender: TObject);
//recalculate the CPU clock
begin
application.processmessages;
setCPUclock;
end;
end.
The Projects (Delphi-7) Click [here] to download the Delphi project for versions that support 64 bit integers (Int64)Click [here] to download the Delphi project for versions that do not support 64 bit integers The CPU clock speed is calculated at formactivate time. Do not use formcreate as this (for some reason) yields a lower clock frequency. The projects allow for display of the CPU clock ticks and also have a button which measures a simple process. Also a button is added to recalculate the CPU clock frequency. Note, that in most cases the measured time (and calculated CPU clock speed) is accurate within 0,1%. But in rare cases, clock speed is much lower because the measuring process apparently was interrupted by Windows.
|
||||||
![]() |
![]() |