What is Free Pascal's equivalent of Delphi's TStopWatch?
Asked Answered
C

2

12

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?

Cambium answered 18/12, 2012 at 6:13 Comment(5)
Depending on your needs, you may have enough by calling QueryPerformanceFrequency/QueryPerformanceCounter in windows.Linzy
Sure I can write my own wrapper for QueryPerformanceXXX API, maybe Free Pascal has its own crossplatform solution.Cambium
I'm not used to freepascal. This code.google.com/p/phocis/source/browse/trunk/lib/… is labled "Early beta: various freepascal functiontions and "stuf""Printery
This class implementation of TStopWatch is also described here, high-performance-timer-tstopwatch.Hydra
I always use the now-Function for that, I don't think there is any better platform independent solution. (Lazarus provides a GetTickCount function, but on Linux that just calls now)Residence
B
10

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.
Bos answered 18/12, 2012 at 14:14 Comment(1)
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
B
5

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.

Buffer answered 18/12, 2012 at 17:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.