Quick.Chrono.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. { ***************************************************************************
  2. Copyright (c) 2015-2018 Kike Pérez
  3. Unit : Quick.Chrono
  4. Description : Chronometers time elapsed and estimated time to do a task
  5. Author : Kike Pérez
  6. Version : 1.4
  7. Created : 27/08/2015
  8. Modified : 07/04/2018
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.Chrono;
  22. interface
  23. {$i QuickLib.inc}
  24. uses
  25. {$IF defined(MSWINDOWS)}
  26. Windows,
  27. {$ELSEIF defined(MACOS)}
  28. Macapi.Mach,
  29. {$ELSEIF defined(POSIX)}
  30. Posix.Time,
  31. {$ENDIF}
  32. SysUtils,
  33. DateUtils;
  34. resourcestring
  35. strDAY = 'day';
  36. strHOUR = 'hour';
  37. strMINUTE = 'minute';
  38. strSECOND = 'second';
  39. strMILLISECOND = 'millisecond';
  40. strMICROSECOND = 'microsecond';
  41. strNANOSECOND = 'nanosecond';
  42. strFMTSHORT_HOURS_MINUTES = 'hh:nn:ss';
  43. strFMTSHORT_MINUTES_SECONDS = 'hh:nn:ss';
  44. strFMTLONG_HOURS_MINUTES = 'h "hour(s) and" n "minute(s)"';
  45. strFMTLONG_MINUTES_SECONDS = 'n "minute(s) and" s "second(s)"';
  46. type
  47. {$IFDEF ANDROID}
  48. TLargeInteger = Int64;
  49. {$ENDIF}
  50. TTimeValue = (utDay, utHour, utMinute, utSecond, utMillisecond,utMicrosecond,utNanosecond);
  51. TTimeFmt = (tfHoursAndMinutes, tfMinutesAndSeconds);
  52. TPrecissionFormat = (pfFloat, pfRound, pfTruncate);
  53. const
  54. UnitShortTime : array[utDay..utNanosecond] of string = ('d','h','m','s','ms','μs','ns');
  55. UnitLongTime : array[utDay..utNanosecond] of string = (strDAY,strHOUR,strMINUTE,strSECOND,strMILLISECOND,strMICROSECOND,strNANOSECOND);
  56. FmtShortTime : array[tfHoursAndMinutes..tfMinutesAndSeconds] of string = (strFMTSHORT_HOURS_MINUTES,strFMTSHORT_MINUTES_SECONDS);
  57. FmtLongTime : array[tfHoursAndMinutes..tfMinutesAndSeconds] of string = (strFMTLONG_HOURS_MINUTES,strFMTLONG_MINUTES_SECONDS);
  58. {$IFDEF FPC}
  59. SecsPerHour = 3600;
  60. {$ENDIF}
  61. type
  62. TChronometer = class
  63. private
  64. fFrequency: TLargeInteger;
  65. fUnitsPerMiS : Int64;
  66. fIsRunning: Boolean;
  67. fIsHighResolution: Boolean;
  68. fStartCount, fStopCount: TLargeInteger;
  69. fStartBreakPoint, fStopBreakPoint : TLargeInteger;
  70. fReportFormatPrecission : TPrecissionFormat;
  71. class function Precission(aValue : Extended; FormatPrecission : TPrecissionFormat) : Extended;
  72. procedure SetTickStamp(var lInt: TLargeInteger);
  73. function GetElapsedTicks: TLargeInteger;
  74. function GetElapsedMilliseconds: TLargeInteger;
  75. function GetElapsedMillisecondsWithPrecission: Extended;
  76. function GetElapsedMilliseconds_BreakPoint: TLargeInteger;
  77. function GetElapsedMillisecondsWithPrecission_BreakPoint: Extended;
  78. function GetElapsedSeconds : TLargeInteger;
  79. class function GetUnitTime(TimeValue : TTimeValue; LongFormat : Boolean) : string;
  80. class function GetFmtTime(TimeFmt : TTimeFmt; LongFormat : Boolean) : string;
  81. public
  82. constructor Create(const StartOnCreate: Boolean = false);
  83. procedure Start;
  84. procedure Stop;
  85. procedure Reset;
  86. procedure Check;
  87. procedure BreakPoint;
  88. property IsHighResolution: Boolean read fIsHighResolution;
  89. property IsRunning: Boolean read fIsRunning;
  90. property ReportFormatPrecission: TPrecissionFormat read fReportFormatPrecission write fReportFormatPrecission;
  91. property ElapsedTicks: TLargeInteger read GetElapsedTicks;
  92. property ElapsedMilliseconds: TLargeInteger read GetElapsedMilliseconds;
  93. property ElapsedMilliseconds_Breakpoint: TLargeInteger read GetElapsedMilliseconds_BreakPoint;
  94. property ElapsedMillisecondsWithPrecission: Extended read GetElapsedMillisecondsWithPrecission;
  95. property ElapsedMillisecondsWithPrecission_BreakPoint: Extended read GetElapsedMillisecondsWithPrecission_BreakPoint;
  96. property ElapsedSeconds: TLargeInteger read GetElapsedSeconds;
  97. function ElapsedTime(LongFormat : Boolean = False) : string;
  98. function ElapsedTime_BreakPoint(LongFormat : Boolean = False) : string;
  99. class function MillisecondsToString(aMilliseconds : TLargeInteger; LongFormat : Boolean = False) : string; overload;
  100. class function MillisecondsToString(aMilliseconds : Extended; FormatPrecission : TPrecissionFormat = pfFloat; LongFormat : Boolean = False) : string; overload;
  101. end;
  102. TChronoBenchmark = class
  103. private
  104. fTotalProcess : Int64;
  105. fLastUpdateTime : TDateTime;
  106. fCurrentProcess : Int64;
  107. fFirstUpdateTime : TDateTime;
  108. fEstimatedMilliseconds : TLargeInteger;
  109. fSpeed : Single;
  110. procedure SetCurrentProcess(NewCurrentProcess : Int64);
  111. function GetElapsedMilliseconds : TLargeInteger;
  112. public
  113. constructor Create;
  114. property TotalProcess : Int64 read fTotalProcess write fTotalProcess;
  115. property CurrentProcess : Int64 read fCurrentProcess write SetCurrentProcess;
  116. property Speed : Single read fSpeed write fSpeed;
  117. property ElapsedMilliseconds : TLargeInteger read GetElapsedMilliseconds;
  118. property EstimatedMilliseconds : TLargeInteger read fEstimatedMilliseconds write fEstimatedMilliseconds;
  119. function ElapsedTime(LongFormat : Boolean) : string;
  120. function EstimatedTime(LongFormat : Boolean) : string;
  121. procedure Reset;
  122. end;
  123. implementation
  124. { TChronometer Class }
  125. constructor TChronometer.Create(const StartOnCreate: Boolean = false);
  126. begin
  127. inherited Create;
  128. fIsRunning := False;
  129. fIsHighResolution := QueryPerformanceFrequency(fFrequency);
  130. fReportFormatPrecission := pfFloat;
  131. fStartCount := 0;
  132. fStopCount := 0;
  133. fStartBreakPoint := 0;
  134. fStopBreakPoint := 0;
  135. if not fIsHighResolution then fFrequency := MSecsPerSec;
  136. fUnitsPerMiS := fFrequency div 1000000;
  137. if StartOnCreate then Start;
  138. end;
  139. function TChronometer.GetElapsedTicks: TLargeInteger;
  140. begin
  141. Result := fStopCount - fStartCount;
  142. end;
  143. procedure TChronometer.SetTickStamp(var lInt: TLargeInteger);
  144. begin
  145. if fIsHighResolution then QueryPerformanceCounter(lInt)
  146. else lInt := MilliSecondOf(Now);
  147. end;
  148. function TChronometer.ElapsedTime(LongFormat : Boolean = False) : string;
  149. begin
  150. Result := MillisecondsToString(ElapsedMillisecondsWithPrecission,fReportFormatPrecission,LongFormat);
  151. end;
  152. function TChronometer.ElapsedTime_BreakPoint(LongFormat : Boolean = False) : string;
  153. begin
  154. Result := MillisecondsToString(ElapsedMillisecondsWithPrecission_BreakPoint,fReportFormatPrecission,True);
  155. end;
  156. class function TChronometer.GetUnitTime(TimeValue : TTimeValue; LongFormat : Boolean) : string;
  157. begin
  158. if LongFormat then Result := ' ' + UnitLongTime[TimeValue] + '(s)'
  159. else Result := UnitShortTime[TimeValue];
  160. end;
  161. class function TChronometer.GetFmtTime(TimeFmt : TTimeFmt; LongFormat : Boolean) : string;
  162. begin
  163. if LongFormat then Result := FmtLongTime[TimeFmt]
  164. else Result := FmtShortTime[TimeFmt];
  165. end;
  166. class function TChronometer.MillisecondsToString(aMilliseconds : TLargeInteger; LongFormat : Boolean = False) : string;
  167. begin
  168. Result := MillisecondsToString(aMilliseconds.ToExtended,pfTruncate,LongFormat);
  169. end;
  170. class function TChronometer.Precission(aValue : Extended; FormatPrecission : TPrecissionFormat) : Extended;
  171. begin
  172. case FormatPrecission of
  173. pfRound : Result := Round(aValue).ToExtended;
  174. pfTruncate : Result := Int(aValue);
  175. else Result := aValue;
  176. end;
  177. end;
  178. class function TChronometer.MillisecondsToString(aMilliseconds : Extended; FormatPrecission : TPrecissionFormat = pfFloat; LongFormat : Boolean = False) : string;
  179. var
  180. dt : TDateTime;
  181. mc : Extended;
  182. begin
  183. if aMilliseconds < 1.0 then
  184. begin
  185. mc := frac(aMilliseconds) * 1000;
  186. if Int(mc) = 0 then Result := Format('%d%s',[Trunc(frac(mc) * 1000),GetUnitTime(utNanosecond,LongFormat)]) //nanoseconds
  187. else Result := Format('%d%s',[Trunc(mc),GetUnitTime(utMicrosecond,LongFormat)]); //microseconds
  188. end
  189. else
  190. begin
  191. if aMilliseconds < MSecsPerSec then //milliseconds
  192. begin
  193. aMilliseconds := Precission(aMilliseconds,FormatPrecission);
  194. if (FormatPrecission = pfFloat) or (frac(aMilliseconds) > 0) then Result := Format('%f%s',[aMilliseconds,GetUnitTime(utMillisecond,LongFormat)])
  195. else Result := Format('%d%s',[Trunc(aMilliseconds),GetUnitTime(utMillisecond,LongFormat)])
  196. end
  197. else if (aMilliseconds / MSecsPerSec) < SecsPerMin then //seconds
  198. begin
  199. aMilliseconds := Precission((aMilliseconds / MSecsPerSec),FormatPrecission);
  200. if (FormatPrecission = pfFloat) or (frac(aMilliseconds) > 0) then Result := Format('%f%s',[aMilliseconds,GetUnitTime(utSecond,LongFormat)])
  201. else Result := Format('%d%s',[Trunc(aMilliseconds),GetUnitTime(utSecond,LongFormat)]);
  202. end
  203. else if ((aMilliseconds / MSecsPerSec) < SecsPerHour) and ((Round(aMilliseconds) mod (SecsPerMin * MSecsPerSec)) = 0) then //minutes
  204. begin
  205. aMilliseconds := Precission((aMilliseconds / (SecsPerMin * MSecsPerSec)),FormatPrecission);
  206. if (FormatPrecission = pfFloat) or (frac(aMilliseconds) > 0) then Result := Format('%f%s',[aMilliseconds,GetUnitTime(utMinute,LongFormat)])
  207. else Result := Format('%d%s',[Trunc(aMilliseconds),GetUnitTime(utMinute,LongFormat)])
  208. end
  209. else if (aMilliseconds / MSecsPerSec) < SecsPerDay then //hours
  210. begin
  211. dt := aMilliseconds / MSecsPerSec / SecsPerDay;
  212. if LongFormat then
  213. begin
  214. if (aMilliseconds / MSecsPerSec) > SecsPerHour then Result := FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt))
  215. else Result := FormatDateTime(GetFmtTime(tfMinutesAndSeconds,LongFormat),Frac(dt));
  216. end
  217. else
  218. begin
  219. Result := FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt));
  220. end;
  221. end
  222. else //días
  223. begin
  224. dt := aMilliseconds / MSecsPerSec / SecsPerDay;
  225. Result := Format('%d%s, %s', [trunc(dt),GetUnitTime(utDay,LongFormat),FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt))]);
  226. end;
  227. end;
  228. end;
  229. function TChronometer.GetElapsedMilliseconds : TLargeInteger;
  230. begin
  231. result := (MSecsPerSec * (fStopCount - fStartCount)) div fFrequency;
  232. end;
  233. function TChronometer.GetElapsedMilliseconds_BreakPoint : TLargeInteger;
  234. begin
  235. result := (MSecsPerSec * (fStopBreakPoint - fStartBreakPoint)) div fFrequency;
  236. end;
  237. function TChronometer.GetElapsedMillisecondsWithPrecission : Extended;
  238. begin
  239. result := (MSecsPerSec * (fStopCount - fStartCount)) / fFrequency;
  240. end;
  241. function TChronometer.GetElapsedMillisecondsWithPrecission_BreakPoint : Extended;
  242. begin
  243. result := (MSecsPerSec * (fStopBreakPoint - fStartBreakPoint)) / fFrequency;
  244. end;
  245. function TChronometer.GetElapsedSeconds : TLargeInteger;
  246. begin
  247. result := ((MSecsPerSec * (fStopCount - fStartCount)) div fFrequency) div MSecsPerSec;
  248. end;
  249. procedure TChronometer.Start;
  250. begin
  251. SetTickStamp(fStartCount);
  252. fIsRunning := true;
  253. end;
  254. procedure TChronometer.Stop;
  255. begin
  256. SetTickStamp(fStopCount);
  257. fIsRunning := false;
  258. end;
  259. procedure TChronometer.Reset;
  260. begin
  261. SetTickStamp(fStartCount);
  262. end;
  263. procedure TChronometer.Check;
  264. begin
  265. if fIsRunning then SetTickStamp(fStopCount);
  266. end;
  267. procedure TChronometer.BreakPoint;
  268. begin
  269. if fIsRunning then
  270. begin
  271. if fStartBreakPoint = 0 then
  272. begin
  273. SetTickStamp(fStopBreakPoint);
  274. fStartBreakPoint := fStartCount;
  275. end
  276. else
  277. begin
  278. fStartBreakPoint := fStopBreakPoint;
  279. SetTickStamp(fStopBreakPoint);
  280. end;
  281. end
  282. else fStopBreakPoint := fStopCount;
  283. end;
  284. { TChronoBenchmark Class }
  285. constructor TChronoBenchmark.Create;
  286. begin
  287. inherited;
  288. fTotalProcess := 0;
  289. fSpeed := 0;
  290. fLastUpdateTime := Now();
  291. fCurrentProcess := 0;
  292. fFirstUpdateTime := 0;
  293. fEstimatedMilliseconds := 0;
  294. end;
  295. procedure TChronoBenchmark.SetCurrentProcess(NewCurrentProcess : Int64);
  296. begin
  297. //corrects first time run
  298. if fLastUpdateTime = 0 then fLastUpdateTime := Now();
  299. if fFirstUpdateTime = 0 then fFirstUpdateTime := Now();
  300. //calculates operation speed
  301. fSpeed := (NewCurrentProcess - fCurrentProcess) / ((Now() - fLastUpdateTime) * 86400);
  302. if fSpeed = 0 then fSpeed := 0.1;
  303. //returns estimated time string
  304. fEstimatedMilliseconds := Round(((TotalProcess - NewCurrentProcess) / fSpeed) * 1000);
  305. //save current values
  306. fLastUpdateTime := Now();
  307. fCurrentProcess := NewCurrentProcess;
  308. end;
  309. function TChronoBenchmark.GetElapsedMilliseconds : TLargeInteger;
  310. begin
  311. Result := Round(((Now() - fFirstUpdateTime) * 86400 * 1000));
  312. end;
  313. function TChronoBenchmark.ElapsedTime(LongFormat : Boolean) : string;
  314. begin
  315. Result := TChronometer.MillisecondsToString(GetElapsedMilliseconds,LongFormat);
  316. end;
  317. function TChronoBenchmark.EstimatedTime(LongFormat : Boolean) : string;
  318. begin
  319. Result := TChronometer.MillisecondsToString(fEstimatedMilliseconds,LongFormat);
  320. end;
  321. procedure TChronoBenchmark.Reset;
  322. begin
  323. fLastUpdateTime := Now();
  324. fSpeed := 0;
  325. fCurrentProcess := 0;
  326. fFirstUpdateTime := 0;
  327. fEstimatedMilliseconds := 0;
  328. end;
  329. end.