Er is een nieuw artikel waarin een timer component wordt beschreven, kijk [ HIER ] Klik [hier] voor het Delphi project als 64 bits integers worden ondersteund (Int64) Klik [hier] voor het Delphi project als geen 64 bits integers worden ondersteund Inleiding In Delphi programma's kan voor tijdmeting gebruik worden gemaakt van een milliseconden teller.Deze 32 variable van het type cardinal levert de tijd in milliseconden vanaf het opstarten van windows. (Er treedt dus een overflow op na 49 dagen) De nauwkeurigheid is niet erg goed, omdat de teller niet elke milliseconde wordt verhoogd, maar slechts 60 maal per seconde. Het volgende programmaatje toont dit gedrag van de milliseconde teller: procedure TForm1.Button1Click(Sender: TObject); var t : cardinal; begin t := gettickcount; while gettickcount = t do; label1.Caption := inttostr(gettickcount-t); end;Zoals blijkt, toont label1 meestal een waarde van 16 en soms van 15. De meeste processen duren minder dan milliseconden, zodat voor een nauwkeurige tijdmeting een proces honderden malen herhaald moet worden. De Pentium processor heeft echter ook een 64 bit teller, die elke clock cycle wordt verhoogd. De RDTSC instructie (code $0F,$31) kopieert bits 0..31 naar register EAX en bits 32..63 naar register EDX. Hiermee is nauwkeurige tijdmeting mogelijk van kort durende processen. Maar er is nog een probleempje: de CPU kloksnelheid kan per systeem verschillen. Om tijd te kunnen meten, moet eerst de klokfrequentie worden berekend. Hiervoor maken we éénmalig gebruik van de milliseconde teller. Dat gaat als volgt: Lees het 64 bit CPU klokregister, wacht 500 milliseconden en lees het 64 bit register opnieuw. Door het klokverschil * 0,001 te delen door 500 ontstaat de kloksnelheid in megahertz. Is eenmaal de kloksnelheid bekend, dat wordt tijdsduur gemeten door:
- het te meten proces uit te voeren - de CPU klok opnieuw te lezen - het verschil van de klokwaardes te berekenen - dit verschil om te zetten naar "double" floating point formaat - dit verschil te delen door de berekende klokfrequentie
2. de Delphi versie ondersteunt geen 64 bit integers Support van 64 bit integers zie de listing hieronder: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 niet ondersteund In dit geval wordt een 64 bit integertype genaamd I64 gedefinieerd.type I64 = array[0..1] of cardinal function I64ToFloat(a : I64) : double converteert het nieuwe I64 formaat naar floating point double. procedure Diff64(t2,t1 : I64) berekent het verschil t2 := t2 - t1 Hier staat de 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. De projecten (Delphi-7) De CPU kloksnelheid wordt berekend na het event formactivate.Gebruik hiervoor niet formcreate want dit levert een te lage waarde op om een of andere reden. De projecten berekenen de tijdsduur van een simpel proces en tonen ook de CPU klokwaarden. Merk op, dat de kloksnelheid meestal binnen 0,1% nauwkeurig wordt berekend. Maar in zeldzame gevallen is de berekende kloksnelheid lager omdat blijkbaar tijdens het meetproces een taak van windows werd afgehandeld. |
||||||