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. |
||||||