123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- { ***************************************************************************
- Copyright (c) 2015-2021 Kike Pérez
- Unit : Quick.Chrono
- Description : Chronometers time elapsed and estimated time to do a task
- Author : Kike Pérez
- Version : 1.5
- Created : 27/08/2015
- Modified : 06/05/2021
- This file is part of QuickLib: https://github.com/exilon/QuickLib
- ***************************************************************************
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- *************************************************************************** }
- unit Quick.Chrono;
- interface
- {$IFDEF DELPHIXE7_UP}
- {$HPPEMIT LEGACYHPP}
- {$ENDIF}
- {$i QuickLib.inc}
- uses
- Classes,
- {$IF defined(MSWINDOWS)}
- Windows,
- {$ELSEIF defined(MACOS)}
- Macapi.Mach,
- {$ELSEIF defined(POSIX)}
- Posix.Time,
- {$ENDIF}
- {$IFDEF FPC}
- {$IFDEF LINUX}
- unixtype, linux,
- {$ENDIF}
- {$ELSE}
- System.TimeSpan,
- {$ENDIF}
- SysUtils,
- DateUtils;
- resourcestring
- strDAY = 'day';
- strHOUR = 'hour';
- strMINUTE = 'minute';
- strSECOND = 'second';
- strMILLISECOND = 'millisecond';
- strMICROSECOND = 'microsecond';
- strNANOSECOND = 'nanosecond';
- strFMTSHORT_HOURS_MINUTES = 'hh:nn:ss';
- strFMTSHORT_MINUTES_SECONDS = 'hh:nn:ss';
- strFMTLONG_HOURS_MINUTES = 'h "hour(s) and" n "minute(s)"';
- strFMTLONG_MINUTES_SECONDS = 'n "minute(s) and" s "second(s)"';
- type
- TTimeValue = (utDay, utHour, utMinute, utSecond, utMillisecond,utMicrosecond,utNanosecond);
- TTimeFmt = (tfHoursAndMinutes, tfMinutesAndSeconds);
- TPrecissionFormat = (pfFloat, pfRound, pfTruncate);
- const
- UnitShortTime : array[utDay..utNanosecond] of string = ('d','h','m','s','ms','μs','ns');
- UnitLongTime : array[utDay..utNanosecond] of string = (strDAY,strHOUR,strMINUTE,strSECOND,strMILLISECOND,strMICROSECOND,strNANOSECOND);
- FmtShortTime : array[tfHoursAndMinutes..tfMinutesAndSeconds] of string = (strFMTSHORT_HOURS_MINUTES,strFMTSHORT_MINUTES_SECONDS);
- FmtLongTime : array[tfHoursAndMinutes..tfMinutesAndSeconds] of string = (strFMTLONG_HOURS_MINUTES,strFMTLONG_MINUTES_SECONDS);
- {$IFDEF FPC}
- SecsPerHour = 3600;
- {$ENDIF}
- type
- IChronometer = interface
- ['{F742C1AD-69DF-4EAA-AB0D-6E571C887901}']
- function GetIsRunning : Boolean;
- function GetElapsedTicks: Int64;
- function GetElapsedMilliseconds: Int64;
- function GetElapsedMillisecondsWithPrecission: Extended;
- function GetElapsedMilliseconds_BreakPoint: Int64;
- function GetElapsedMillisecondsWithPrecission_BreakPoint: Extended;
- function GetElapsedSeconds : Int64;
- property IsRunning: Boolean read GetIsRunning;
- procedure Start;
- procedure Stop;
- procedure Reset;
- procedure Check;
- procedure BreakPoint;
- property ElapsedTicks: Int64 read GetElapsedTicks;
- property ElapsedMilliseconds: Int64 read GetElapsedMilliseconds;
- property ElapsedMilliseconds_Breakpoint: Int64 read GetElapsedMilliseconds_BreakPoint;
- property ElapsedMillisecondsWithPrecission: Extended read GetElapsedMillisecondsWithPrecission;
- property ElapsedMillisecondsWithPrecission_BreakPoint: Extended read GetElapsedMillisecondsWithPrecission_BreakPoint;
- property ElapsedSeconds: Int64 read GetElapsedSeconds;
- function ElapsedTime(LongFormat : Boolean = False) : string;
- function ElapsedTime_BreakPoint(LongFormat : Boolean = False) : string;
- end;
- TChronometer = class(TInterfacedObject,IChronometer)
- private
- fFrequency: Int64;
- fIsRunning: Boolean;
- fIsHighResolution: Boolean;
- fStartCount, fStopCount: Int64;
- fStartBreakPoint, fStopBreakPoint : Int64;
- fReportFormatPrecission : TPrecissionFormat;
- class function Precission(aValue : Extended; FormatPrecission : TPrecissionFormat) : Extended;
- function GetTickStamp : Int64;
- function GetElapsedTicks: Int64;
- function GetElapsedMilliseconds: Int64;
- function GetElapsedMillisecondsWithPrecission: Extended;
- function GetElapsedMilliseconds_BreakPoint: Int64;
- function GetElapsedMillisecondsWithPrecission_BreakPoint: Extended;
- function GetElapsedSeconds : Int64;
- class function GetUnitTime(TimeValue : TTimeValue; LongFormat : Boolean) : string;
- class function GetFmtTime(TimeFmt : TTimeFmt; LongFormat : Boolean) : string;
- function GetIsRunning: Boolean;
- public
- constructor Create(const StartOnCreate: Boolean = false);
- class function NewChrono(const StartOnCreate: Boolean = True) : IChronometer;
- procedure Start;
- procedure Stop;
- procedure Reset;
- procedure Check;
- procedure BreakPoint;
- property IsHighResolution: Boolean read fIsHighResolution;
- property IsRunning: Boolean read GetIsRunning;
- property ReportFormatPrecission: TPrecissionFormat read fReportFormatPrecission write fReportFormatPrecission;
- property ElapsedTicks: Int64 read GetElapsedTicks;
- property ElapsedMilliseconds: Int64 read GetElapsedMilliseconds;
- property ElapsedMilliseconds_Breakpoint: Int64 read GetElapsedMilliseconds_BreakPoint;
- property ElapsedMillisecondsWithPrecission: Extended read GetElapsedMillisecondsWithPrecission;
- property ElapsedMillisecondsWithPrecission_BreakPoint: Extended read GetElapsedMillisecondsWithPrecission_BreakPoint;
- property ElapsedSeconds: Int64 read GetElapsedSeconds;
- function ElapsedTime(LongFormat : Boolean = False) : string;
- function ElapsedTime_BreakPoint(LongFormat : Boolean = False) : string;
- class function MillisecondsToString(aMilliseconds : Int64; LongFormat : Boolean = False) : string; overload;
- class function MillisecondsToString(aMilliseconds : Extended; FormatPrecission : TPrecissionFormat = pfFloat; LongFormat : Boolean = False) : string; overload;
- end;
- TChronoBenchmark = class
- private
- fTotalProcess : Int64;
- fLastUpdateTime : TDateTime;
- fCurrentProcess : Int64;
- fFirstUpdateTime : TDateTime;
- fEstimatedMilliseconds : Int64;
- fSpeed : Single;
- procedure SetCurrentProcess(NewCurrentProcess : Int64);
- function GetElapsedMilliseconds : Int64;
- public
- constructor Create;
- property TotalProcess : Int64 read fTotalProcess write fTotalProcess;
- property CurrentProcess : Int64 read fCurrentProcess write SetCurrentProcess;
- property Speed : Single read fSpeed write fSpeed;
- property ElapsedMilliseconds : Int64 read GetElapsedMilliseconds;
- property EstimatedMilliseconds : Int64 read fEstimatedMilliseconds write fEstimatedMilliseconds;
- function ElapsedTime(LongFormat : Boolean) : string;
- function EstimatedTime(LongFormat : Boolean) : string;
- procedure Reset;
- end;
- implementation
- { TChronometer Class }
- constructor TChronometer.Create(const StartOnCreate: Boolean = false);
- begin
- inherited Create;
- fIsRunning := False;
- fReportFormatPrecission := pfFloat;
- fStartCount := 0;
- fStopCount := 0;
- fStartBreakPoint := 0;
- fStopBreakPoint := 0;
- {$IF Defined(MSWINDOWS)}
- if not QueryPerformanceFrequency(fFrequency) then
- begin
- fIsHighResolution := False;
- //fFrequency := TTimeSpan.TicksPerSecond;
- fFrequency := MSecsPerSec;
- //TickFrequency := 1.0;
- end else
- begin
- fIsHighResolution := True;
- //TickFrequency := 10000000.0 / fFrequency;
- end;
- {$ELSEIF Defined(POSIX) OR Defined(LINUX)}
- fIsHighResolution := True;
- fFrequency := 10000000;
- //TickFrequency := 10000000.0 / fFrequency;
- {$ENDIF}
- if StartOnCreate then Start;
- end;
- function TChronometer.GetElapsedTicks: Int64;
- begin
- Result := fStopCount - fStartCount;
- end;
- function TChronometer.GetTickStamp : Int64;
- {$IF (Defined(POSIX) OR Defined(LINUX)) AND NOT Defined(MACOS)}
- var
- res: timespec;
- {$ENDIF}
- begin
- {$IFDEF MSWINDOWS}
- if fIsHighResolution then QueryPerformanceCounter(Result)
- else Result := MilliSecondOf(Now);
- {$ELSE}
- {$IFDEF MACOS}
- Result := Int64(AbsoluteToNanoseconds(mach_absolute_time) div 100);
- {$ENDIF}
- {$IF (Defined(POSIX) OR Defined(LINUX)) AND NOT Defined(MACOS)}
- clock_gettime(CLOCK_MONOTONIC, @res);
- Result := (Int64(1000000000) * res.tv_sec + res.tv_nsec) div 100;
- {$ENDIF}
- {$ENDIF}
- end;
- function TChronometer.ElapsedTime(LongFormat : Boolean = False) : string;
- begin
- Result := MillisecondsToString(ElapsedMillisecondsWithPrecission,fReportFormatPrecission,LongFormat);
- end;
- function TChronometer.ElapsedTime_BreakPoint(LongFormat : Boolean = False) : string;
- begin
- Result := MillisecondsToString(ElapsedMillisecondsWithPrecission_BreakPoint,fReportFormatPrecission,LongFormat);
- end;
- class function TChronometer.GetUnitTime(TimeValue : TTimeValue; LongFormat : Boolean) : string;
- begin
- if LongFormat then Result := ' ' + UnitLongTime[TimeValue] + '(s)'
- else Result := UnitShortTime[TimeValue];
- end;
- class function TChronometer.GetFmtTime(TimeFmt : TTimeFmt; LongFormat : Boolean) : string;
- begin
- if LongFormat then Result := FmtLongTime[TimeFmt]
- else Result := FmtShortTime[TimeFmt];
- end;
- function TChronometer.GetIsRunning: Boolean;
- begin
- Result := fIsRunning;
- end;
- class function TChronometer.NewChrono(const StartOnCreate: Boolean = True) : IChronometer;
- begin
- Result := TChronometer.Create(StartOnCreate);
- end;
- class function TChronometer.MillisecondsToString(aMilliseconds : Int64; LongFormat : Boolean = False) : string;
- begin
- Result := MillisecondsToString(aMilliseconds.ToExtended,pfTruncate,LongFormat);
- end;
- class function TChronometer.Precission(aValue : Extended; FormatPrecission : TPrecissionFormat) : Extended;
- begin
- case FormatPrecission of
- pfRound : Result := Round(aValue).ToExtended;
- pfTruncate : Result := Int(aValue);
- else Result := aValue;
- end;
- end;
- class function TChronometer.MillisecondsToString(aMilliseconds : Extended; FormatPrecission : TPrecissionFormat = pfFloat; LongFormat : Boolean = False) : string;
- var
- dt : TDateTime;
- mc : Extended;
- begin
- if aMilliseconds < 1.0 then
- begin
- mc := frac(aMilliseconds) * 1000;
- if Int(mc) = 0 then Result := Format('%d%s',[Trunc(frac(mc) * 1000),GetUnitTime(utNanosecond,LongFormat)]) //nanoseconds
- else Result := Format('%d%s',[Trunc(mc),GetUnitTime(utMicrosecond,LongFormat)]); //microseconds
- end
- else
- begin
- if aMilliseconds < MSecsPerSec then //milliseconds
- begin
- aMilliseconds := Precission(aMilliseconds,FormatPrecission);
- if (FormatPrecission = pfFloat) or (frac(aMilliseconds) > 0) then Result := Format('%f%s',[aMilliseconds,GetUnitTime(utMillisecond,LongFormat)])
- else Result := Format('%d%s',[Trunc(aMilliseconds),GetUnitTime(utMillisecond,LongFormat)])
- end
- else if (aMilliseconds / MSecsPerSec) < SecsPerMin then //seconds
- begin
- aMilliseconds := Precission((aMilliseconds / MSecsPerSec),FormatPrecission);
- if (FormatPrecission = pfFloat) or (frac(aMilliseconds) > 0) then Result := Format('%f%s',[aMilliseconds,GetUnitTime(utSecond,LongFormat)])
- else Result := Format('%d%s',[Trunc(aMilliseconds),GetUnitTime(utSecond,LongFormat)]);
- end
- else if ((aMilliseconds / MSecsPerSec) < SecsPerHour) and ((Round(aMilliseconds) mod (SecsPerMin * MSecsPerSec)) = 0) then //minutes
- begin
- aMilliseconds := Precission((aMilliseconds / (SecsPerMin * MSecsPerSec)),FormatPrecission);
- if (FormatPrecission = pfFloat) or (frac(aMilliseconds) > 0) then Result := Format('%f%s',[aMilliseconds,GetUnitTime(utMinute,LongFormat)])
- else Result := Format('%d%s',[Trunc(aMilliseconds),GetUnitTime(utMinute,LongFormat)])
- end
- else if (aMilliseconds / MSecsPerSec) < SecsPerDay then //hours
- begin
- dt := aMilliseconds / MSecsPerSec / SecsPerDay;
- if LongFormat then
- begin
- if (aMilliseconds / MSecsPerSec) > SecsPerHour then Result := FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt))
- else Result := FormatDateTime(GetFmtTime(tfMinutesAndSeconds,LongFormat),Frac(dt));
- end
- else
- begin
- Result := FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt));
- end;
- end
- else //días
- begin
- dt := aMilliseconds / MSecsPerSec / SecsPerDay;
- Result := Format('%d%s, %s', [trunc(dt),GetUnitTime(utDay,LongFormat),FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt))]);
- end;
- end;
- end;
- function TChronometer.GetElapsedMilliseconds : Int64;
- begin
- result := (MSecsPerSec * (fStopCount - fStartCount)) div fFrequency;
- end;
- function TChronometer.GetElapsedMilliseconds_BreakPoint : Int64;
- begin
- result := (MSecsPerSec * (fStopBreakPoint - fStartBreakPoint)) div fFrequency;
- end;
- function TChronometer.GetElapsedMillisecondsWithPrecission : Extended;
- begin
- result := (MSecsPerSec * (fStopCount - fStartCount)) / fFrequency;
- end;
- function TChronometer.GetElapsedMillisecondsWithPrecission_BreakPoint : Extended;
- begin
- result := (MSecsPerSec * (fStopBreakPoint - fStartBreakPoint)) / fFrequency;
- end;
- function TChronometer.GetElapsedSeconds : Int64;
- begin
- result := ((MSecsPerSec * (fStopCount - fStartCount)) div fFrequency) div MSecsPerSec;
- end;
- procedure TChronometer.Start;
- begin
- fStartCount := GetTickStamp;
- fIsRunning := true;
- end;
- procedure TChronometer.Stop;
- begin
- fStopCount := GetTickStamp;
- fIsRunning := false;
- end;
- procedure TChronometer.Reset;
- begin
- fStartCount := GetTickStamp;
- end;
- procedure TChronometer.Check;
- begin
- if fIsRunning then fStopCount := GetTickStamp;
- end;
- procedure TChronometer.BreakPoint;
- begin
- if fIsRunning then
- begin
- if fStartBreakPoint = 0 then
- begin
- fStopBreakPoint := GetTickStamp;
- fStartBreakPoint := fStartCount;
- end
- else
- begin
- fStartBreakPoint := fStopBreakPoint;
- fStopBreakPoint := GetTickStamp;
- end;
- end
- else fStopBreakPoint := fStopCount;
- end;
- { TChronoBenchmark Class }
- constructor TChronoBenchmark.Create;
- begin
- inherited;
- fTotalProcess := 0;
- fSpeed := 0;
- fLastUpdateTime := Now();
- fCurrentProcess := 0;
- fFirstUpdateTime := 0;
- fEstimatedMilliseconds := 0;
- end;
- procedure TChronoBenchmark.SetCurrentProcess(NewCurrentProcess : Int64);
- begin
- //corrects first time run
- if fLastUpdateTime = 0 then fLastUpdateTime := Now();
- if fFirstUpdateTime = 0 then fFirstUpdateTime := Now();
- //calculates operation speed
- fSpeed := (NewCurrentProcess - fCurrentProcess) / ((Now() - fLastUpdateTime) * 86400);
- if fSpeed = 0 then fSpeed := 0.1;
- //returns estimated time string
- fEstimatedMilliseconds := Round(((TotalProcess - NewCurrentProcess) / fSpeed) * 1000);
- //save current values
- fLastUpdateTime := Now();
- fCurrentProcess := NewCurrentProcess;
- end;
- function TChronoBenchmark.GetElapsedMilliseconds : Int64;
- begin
- Result := Round(((Now() - fFirstUpdateTime) * 86400 * 1000));
- end;
- function TChronoBenchmark.ElapsedTime(LongFormat : Boolean) : string;
- begin
- Result := TChronometer.MillisecondsToString(GetElapsedMilliseconds,LongFormat);
- end;
- function TChronoBenchmark.EstimatedTime(LongFormat : Boolean) : string;
- begin
- Result := TChronometer.MillisecondsToString(fEstimatedMilliseconds,LongFormat);
- end;
- procedure TChronoBenchmark.Reset;
- begin
- fLastUpdateTime := Now();
- fSpeed := 0;
- fCurrentProcess := 0;
- fFirstUpdateTime := 0;
- fEstimatedMilliseconds := 0;
- end;
- end.
|