I need to implement simple performance benchmarking in Free Pascal. In Delphi I am using TStopWatch
record from Diagnostics unit, what can I use in Free Pascal/Lazarus?
What is Free Pascal's equivalent of Delphi's TStopWatch?
Asked Answered
Here is an implementation modeled after Delphi online doc:
{ High frequency stop watch implemntation.
Copyright (c) 2012 by Inoussa OUEDRAOGO
This source code is distributed under the Library GNU General Public License
with the following modification:
- object files and libraries linked into an application may be
distributed without source code.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{$IFDEF FPC}
{$mode objfpc}{$H+}
{$modeswitch advancedrecords}
{$ENDIF}
{$IFDEF MSWINDOWS}
{$IFNDEF WINDOWS}
{$DEFINE WINDOWS}
{$ENDIF WINDOWS}
{$ENDIF MSWINDOWS}
unit stopwatch;
interface
uses
SysUtils
{$IFDEF LINUX}
,unixtype, linux
{$ENDIF LINUX}
;
type
{ TStopWatch }
TStopWatch = record
private
const
C_THOUSAND = 1000;
C_MILLION = C_THOUSAND * C_THOUSAND;
C_BILLION = C_THOUSAND * C_THOUSAND * C_THOUSAND;
TicksPerNanoSecond = 100;
TicksPerMilliSecond = 10000;
TicksPerSecond = C_BILLION div 100;
Type
TBaseMesure =
{$IFDEF WINDOWS}
Int64;
{$ENDIF WINDOWS}
{$IFDEF LINUX}
TTimeSpec;
{$ENDIF LINUX}
strict private
class var FFrequency : Int64;
class var FIsHighResolution : Boolean;
strict private
FElapsed : Int64;
FRunning : Boolean;
FStartPosition : TBaseMesure;
strict private
procedure CheckInitialization();inline;
function GetElapsedMilliseconds: Int64;
function GetElapsedTicks: Int64;
public
class function Create() : TStopWatch;static;
class function StartNew() : TStopWatch;static;
class property Frequency : Int64 read FFrequency;
class property IsHighResolution : Boolean read FIsHighResolution;
procedure Reset();
procedure Start();
procedure Stop();
property ElapsedMilliseconds : Int64 read GetElapsedMilliseconds;
property ElapsedTicks : Int64 read GetElapsedTicks;
property IsRunning : Boolean read FRunning;
end;
resourcestring
sStopWatchNotInitialized = 'The StopWatch is not initialized.';
implementation
{$IFDEF WINDOWS}
uses
Windows;
{$ENDIF WINDOWS}
{ TStopWatch }
class function TStopWatch.Create(): TStopWatch;
{$IFDEF LINUX}
var
r : TBaseMesure;
{$ENDIF LINUX}
begin
if (FFrequency = 0) then begin
{$IFDEF WINDOWS}
FIsHighResolution := QueryPerformanceFrequency(FFrequency);
{$ENDIF WINDOWS}
{$IFDEF LINUX}
FIsHighResolution := (clock_getres(CLOCK_MONOTONIC,@r) = 0);
FIsHighResolution := FIsHighResolution and (r.tv_nsec <> 0);
if (r.tv_nsec <> 0) then
FFrequency := C_BILLION div r.tv_nsec;
{$ENDIF LINUX}
end;
FillChar(Result,SizeOf(Result),0);
end;
class function TStopWatch.StartNew() : TStopWatch;
begin
Result := TStopWatch.Create();
Result.Start();
end;
procedure TStopWatch.CheckInitialization();
begin
if (FFrequency = 0) then
raise Exception.Create(sStopWatchNotInitialized);
end;
function TStopWatch.GetElapsedMilliseconds: Int64;
begin
{$IFDEF WINDOWS}
Result := ElapsedTicks * TicksPerMilliSecond;
{$ENDIF WINDOWS}
{$IFDEF LINUX}
Result := FElapsed div C_MILLION;
{$ENDIF LINUX}
end;
function TStopWatch.GetElapsedTicks: Int64;
begin
CheckInitialization();
{$IFDEF WINDOWS}
Result := (FElapsed * TicksPerSecond) div FFrequency;
{$ENDIF WINDOWS}
{$IFDEF LINUX}
Result := FElapsed div TicksPerNanoSecond;
{$ENDIF LINUX}
end;
procedure TStopWatch.Reset();
begin
Stop();
FElapsed := 0;
FillChar(FStartPosition,SizeOf(FStartPosition),0);
end;
procedure TStopWatch.Start();
begin
if FRunning then
exit;
FRunning := True;
{$IFDEF WINDOWS}
QueryPerformanceCounter(FStartPosition);
{$ENDIF WINDOWS}
{$IFDEF LINUX}
clock_gettime(CLOCK_MONOTONIC,@FStartPosition);
{$ENDIF LINUX}
end;
procedure TStopWatch.Stop();
var
locEnd : TBaseMesure;
s, n : Int64;
begin
if not FRunning then
exit;
FRunning := False;
{$IFDEF WINDOWS}
QueryPerformanceCounter(locEnd);
FElapsed := FElapsed + (UInt64(locEnd) - UInt64(FStartPosition));
{$ENDIF WINDOWS}
{$IFDEF LINUX}
clock_gettime(CLOCK_MONOTONIC,@locEnd);
if (locEnd.tv_nsec < FStartPosition.tv_nsec) then begin
s := locEnd.tv_sec - FStartPosition.tv_sec - 1;
n := C_BILLION + locEnd.tv_nsec - FStartPosition.tv_nsec;
end else begin
s := locEnd.tv_sec - FStartPosition.tv_sec;
n := locEnd.tv_nsec - FStartPosition.tv_nsec;
end;
FElapsed := FElapsed + (s * C_BILLION) + n;
{$ENDIF LINUX}
end;
end.
It doesn't work on Windows. Elapsed time can't be read before the stopwatch has been stopped and the resulting values don't show anything useful. –
Preachment
Have a look at TJclCounter from project Jedi. It's implementation is even more sophisticated than Delphi's TStopwatch by taking QueryPerformanceCounter call overhead into account.
© 2022 - 2024 — McMap. All rights reserved.
TStopWatch
is also described here, high-performance-timer-tstopwatch. – Hydra