Home » Tipps & Tricks » Applikation » Debuggen » Zeitmessungen durchführen

Zeitmessungen durchführen

Es kommt immer wieder vor, dass die Geschwindigkeit einer Routine oder eines Algorithmus‘ gemessen werden soll. Dafür gibt es mehrere Ansätze. Die zwei wichtigsten werden hier vorgestellt:

GetTickCount

Mithilfe von GetTickCount kann man gut eine einfache Zeitmessungen durchführen. GetTickCount gibt die Zeit seit dem Systemstart in Millisekunden zurück. Der Rückgabewert ist vom Typ Cardinal (vorzeichenloser 32Bit-Ganzzahltyp). Bestimmt man nun die Differenz von GetTickCount vor und nach Ablauf der Rounine, so kennt man die Zeit, die dazwischen vergangen ist:

procedure TForm1.Button1Click(Sender: TObject);
var
  startTime: Cardinal;
begin
  startTime := GetTickCount;

  //Befehlesfolge deren Zeitdauer bestimmt werden soll

  ShowMessage('Die Routine benötigte etwa ' + IntToStr(GetTickCount - startTime) + 'ms');
end;

QueryPerformanceCounter

Es gibt noch eine andere Möglichkeit, Zeitmessungen durchzuführen und zwar mit dem QueryPerformanceCounter. Die Auflösung, die diese Lösung bietet ist wesentlich höher als bei GetTickCount. Die Frequenz dieses Timers beträgt meist mehrere Millionen Hertz. Dies ergibt Periodendauern im Mikrosekundenbereich und kleiner. Die QueryPerformance-Api eignet sich daher sehr gut zur Performancemessung. Als Timer ist diese Variante aufgrund des dabei notwendigen hohen Ressourcenverbauchs allerdings nicht zu empfehlen.
Die genaue Frequenz des Timers muss zunächst mit QueryPerformanceFrequency abgefragt werden, da sich diese von System zu System unterscheiden kann. Damit diese Zeitmessung funktioniert muss in dem Computer ein „high resolution performance counter“ installiert sein, was bei moderneren Computern eigentlich immer der Fall ist.

procedure TForm1.Button2Click(Sender: TObject);
var
  freq: Int64;
  startTime: Int64;
  endTime: Int64;
begin
  QueryPerformanceFrequency(freq);
  QueryPerformanceCounter(startTime);

  //Befehlesfolge deren Zeitdauer bestimmt werden soll

  QueryPerformanceCounter(endTime);

  ShowMessage('Die Routine benötigte etwa ' + IntToStr((endTime - startTime) * 1000 div freq) + 'ms');
end;

Die Ausgabe erfolgt in Millisekunden. Dazu muss das Ergebnis vorher nur mit 1000 multipliziert werden.
Der Typ Int64 existiert erst ab Delphi 4. Bei älteren Delphi-Versionen muss auf den Typ TLargeInteger zurückgegriffen werden, der ab Delphi 4 mit Int64 identisch ist:

procedure TForm1.Button1Click(Sender: TObject);
var
  freq: TLargeInteger;
  startTime: TLargeInteger;
  endTime: TLargeInteger;
begin
  QueryPerformanceFrequency(freq);
  QueryPerformanceCounter(startTime);

  //Befehlesfolge deren Zeitdauer bestimmt werden soll

  QueryPerformanceCounter(endTime);

  ShowMessage('Die Routine benötigte etwa ' +
    IntToStr(Trunc((endTime.QuadPart - startTime.QuadPart) * 1000 / freq.QuadPart)) + 'ms');
end;

Zu beachten ist hier, dass QueryPerformanceFrequency und QueryPerformanceCounter ihre Ergebnisse im Gegensatz zu GetTickCount nicht als Rückgabewert, sondern über einen var-Parameter zurückliefern.
Auch, wenn sich mit dem QueryPerformanceCounter scheinbar sehr genaue Zeitmessungen durchführen lassen, sollte man sich nicht dazu verleiten lassen zu denken, dass das Ergebnis auch immer so genau wird, wie es theoretisch möglich ist. Andere Prozesse, die im Hintergrund laufen, können das Ergebnis verfälschen. Um verlässlichere Ergebnisse zu erzielen hat es sich bewährt, die zu untersuchende Routine mehrmals hintereinander(je nach Routine auch zehntausende Male) in einer Schleife laufen, zu lassen, sodass die Gesamtzeit bei mehreren Sekunden liegt. Die gemessene Gesamtzeit kann man dann nochmal durch die Anzahl der Schleifendurchgänge teilen. Dies ist bei GetTickCount noch wichtiger als bei QueryPerformanceCounter.