Quick.Chrono.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. { ***************************************************************************
  2. Copyright (c) 2015-2019 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.5
  7. Created : 27/08/2015
  8. Modified : 20/01/2019
  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. {$HPPEMIT LEGACYHPP}
  24. {$i QuickLib.inc}
  25. uses
  26. Classes,
  27. {$IF defined(MSWINDOWS)}
  28. Windows,
  29. {$ELSEIF defined(MACOS)}
  30. Macapi.Mach,
  31. {$ELSEIF defined(POSIX)}
  32. Posix.Time,
  33. {$ENDIF}
  34. {$IFDEF FPC}
  35. {$IFDEF LINUX}
  36. unixtype, linux,
  37. {$ENDIF}
  38. {$ELSE}
  39. System.TimeSpan,
  40. {$ENDIF}
  41. SysUtils,
  42. DateUtils;
  43. resourcestring
  44. strDAY = 'day';
  45. strHOUR = 'hour';
  46. strMINUTE = 'minute';
  47. strSECOND = 'second';
  48. strMILLISECOND = 'millisecond';
  49. strMICROSECOND = 'microsecond';
  50. strNANOSECOND = 'nanosecond';
  51. strFMTSHORT_HOURS_MINUTES = 'hh:nn:ss';
  52. strFMTSHORT_MINUTES_SECONDS = 'hh:nn:ss';
  53. strFMTLONG_HOURS_MINUTES = 'h "hour(s) and" n "minute(s)"';
  54. strFMTLONG_MINUTES_SECONDS = 'n "minute(s) and" s "second(s)"';
  55. type
  56. {$IF Defined(NEXTGEN) OR Defined(LINUX) OR Defined(OSX)}
  57. TLargeInteger = Int64;
  58. {$ENDIF}
  59. TTimeValue = (utDay, utHour, utMinute, utSecond, utMillisecond,utMicrosecond,utNanosecond);
  60. TTimeFmt = (tfHoursAndMinutes, tfMinutesAndSeconds);
  61. TPrecissionFormat = (pfFloat, pfRound, pfTruncate);
  62. const
  63. UnitShortTime : array[utDay..utNanosecond] of string = ('d','h','m','s','ms','μs','ns');
  64. UnitLongTime : array[utDay..utNanosecond] of string = (strDAY,strHOUR,strMINUTE,strSECOND,strMILLISECOND,strMICROSECOND,strNANOSECOND);
  65. FmtShortTime : array[tfHoursAndMinutes..tfMinutesAndSeconds] of string = (strFMTSHORT_HOURS_MINUTES,strFMTSHORT_MINUTES_SECONDS);
  66. FmtLongTime : array[tfHoursAndMinutes..tfMinutesAndSeconds] of string = (strFMTLONG_HOURS_MINUTES,strFMTLONG_MINUTES_SECONDS);
  67. {$IFDEF FPC}
  68. SecsPerHour = 3600;
  69. {$ENDIF}
  70. type
  71. TChronometer = class
  72. private
  73. fFrequency: TLargeInteger;
  74. fIsRunning: Boolean;
  75. fIsHighResolution: Boolean;
  76. fStartCount, fStopCount: TLargeInteger;
  77. fStartBreakPoint, fStopBreakPoint : TLargeInteger;
  78. fReportFormatPrecission : TPrecissionFormat;
  79. class function Precission(aValue : Extended; FormatPrecission : TPrecissionFormat) : Extended;
  80. procedure SetTickStamp(var lInt: TLargeInteger);
  81. function GetElapsedTicks: TLargeInteger;
  82. function GetElapsedMilliseconds: TLargeInteger;
  83. function GetElapsedMillisecondsWithPrecission: Extended;
  84. function GetElapsedMilliseconds_BreakPoint: TLargeInteger;
  85. function GetElapsedMillisecondsWithPrecission_BreakPoint: Extended;
  86. function GetElapsedSeconds : TLargeInteger;
  87. class function GetUnitTime(TimeValue : TTimeValue; LongFormat : Boolean) : string;
  88. class function GetFmtTime(TimeFmt : TTimeFmt; LongFormat : Boolean) : string;
  89. public
  90. constructor Create(const StartOnCreate: Boolean = false);
  91. procedure Start;
  92. procedure Stop;
  93. procedure Reset;
  94. procedure Check;
  95. procedure BreakPoint;
  96. property IsHighResolution: Boolean read fIsHighResolution;
  97. property IsRunning: Boolean read fIsRunning;
  98. property ReportFormatPrecission: TPrecissionFormat read fReportFormatPrecission write fReportFormatPrecission;
  99. property ElapsedTicks: TLargeInteger read GetElapsedTicks;
  100. property ElapsedMilliseconds: TLargeInteger read GetElapsedMilliseconds;
  101. property ElapsedMilliseconds_Breakpoint: TLargeInteger read GetElapsedMilliseconds_BreakPoint;
  102. property ElapsedMillisecondsWithPrecission: Extended read GetElapsedMillisecondsWithPrecission;
  103. property ElapsedMillisecondsWithPrecission_BreakPoint: Extended read GetElapsedMillisecondsWithPrecission_BreakPoint;
  104. property ElapsedSeconds: TLargeInteger read GetElapsedSeconds;
  105. function ElapsedTime(LongFormat : Boolean = False) : string;
  106. function ElapsedTime_BreakPoint(LongFormat : Boolean = False) : string;
  107. class function MillisecondsToString(aMilliseconds : TLargeInteger; LongFormat : Boolean = False) : string; overload;
  108. class function MillisecondsToString(aMilliseconds : Extended; FormatPrecission : TPrecissionFormat = pfFloat; LongFormat : Boolean = False) : string; overload;
  109. end;
  110. TChronoBenchmark = class
  111. private
  112. fTotalProcess : Int64;
  113. fLastUpdateTime : TDateTime;
  114. fCurrentProcess : Int64;
  115. fFirstUpdateTime : TDateTime;
  116. fEstimatedMilliseconds : TLargeInteger;
  117. fSpeed : Single;
  118. procedure SetCurrentProcess(NewCurrentProcess : Int64);
  119. function GetElapsedMilliseconds : TLargeInteger;
  120. public
  121. constructor Create;
  122. property TotalProcess : Int64 read fTotalProcess write fTotalProcess;
  123. property CurrentProcess : Int64 read fCurrentProcess write SetCurrentProcess;
  124. property Speed : Single read fSpeed write fSpeed;
  125. property ElapsedMilliseconds : TLargeInteger read GetElapsedMilliseconds;
  126. property EstimatedMilliseconds : TLargeInteger read fEstimatedMilliseconds write fEstimatedMilliseconds;
  127. function ElapsedTime(LongFormat : Boolean) : string;
  128. function EstimatedTime(LongFormat : Boolean) : string;
  129. procedure Reset;
  130. end;
  131. implementation
  132. { TChronometer Class }
  133. constructor TChronometer.Create(const StartOnCreate: Boolean = false);
  134. begin
  135. inherited Create;
  136. fIsRunning := False;
  137. fReportFormatPrecission := pfFloat;
  138. fStartCount := 0;
  139. fStopCount := 0;
  140. fStartBreakPoint := 0;
  141. fStopBreakPoint := 0;
  142. {$IF Defined(MSWINDOWS)}
  143. if not QueryPerformanceFrequency(fFrequency) then
  144. begin
  145. fIsHighResolution := False;
  146. //fFrequency := TTimeSpan.TicksPerSecond;
  147. fFrequency := MSecsPerSec;
  148. //TickFrequency := 1.0;
  149. end else
  150. begin
  151. fIsHighResolution := True;
  152. //TickFrequency := 10000000.0 / fFrequency;
  153. end;
  154. {$ELSEIF Defined(POSIX) OR Defined(LINUX)}
  155. fIsHighResolution := True;
  156. fFrequency := 10000000;
  157. //TickFrequency := 10000000.0 / fFrequency;
  158. {$ENDIF}
  159. if StartOnCreate then Start;
  160. end;
  161. function TChronometer.GetElapsedTicks: TLargeInteger;
  162. begin
  163. Result := fStopCount - fStartCount;
  164. end;
  165. procedure TChronometer.SetTickStamp(var lInt: TLargeInteger);
  166. {$IF (Defined(POSIX) OR Defined(LINUX)) AND NOT Defined(MACOS)}
  167. var
  168. res: timespec;
  169. {$ENDIF}
  170. begin
  171. {$IFDEF MSWINDOWS}
  172. if fIsHighResolution then QueryPerformanceCounter(lInt)
  173. else lInt := MilliSecondOf(Now);
  174. {$ELSE}
  175. {$IFDEF MACOS}
  176. lInt := Int64(AbsoluteToNanoseconds(mach_absolute_time) div 100);
  177. {$ENDIF}
  178. {$IF (Defined(POSIX) OR Defined(LINUX)) AND NOT Defined(MACOS)}
  179. clock_gettime(CLOCK_MONOTONIC, @res);
  180. lInt := (Int64(1000000000) * res.tv_sec + res.tv_nsec) div 100;
  181. {$ENDIF}
  182. {$ENDIF}
  183. end;
  184. function TChronometer.ElapsedTime(LongFormat : Boolean = False) : string;
  185. begin
  186. Result := MillisecondsToString(ElapsedMillisecondsWithPrecission,fReportFormatPrecission,LongFormat);
  187. end;
  188. function TChronometer.ElapsedTime_BreakPoint(LongFormat : Boolean = False) : string;
  189. begin
  190. Result := MillisecondsToString(ElapsedMillisecondsWithPrecission_BreakPoint,fReportFormatPrecission,True);
  191. end;
  192. class function TChronometer.GetUnitTime(TimeValue : TTimeValue; LongFormat : Boolean) : string;
  193. begin
  194. if LongFormat then Result := ' ' + UnitLongTime[TimeValue] + '(s)'
  195. else Result := UnitShortTime[TimeValue];
  196. end;
  197. class function TChronometer.GetFmtTime(TimeFmt : TTimeFmt; LongFormat : Boolean) : string;
  198. begin
  199. if LongFormat then Result := FmtLongTime[TimeFmt]
  200. else Result := FmtShortTime[TimeFmt];
  201. end;
  202. class function TChronometer.MillisecondsToString(aMilliseconds : TLargeInteger; LongFormat : Boolean = False) : string;
  203. begin
  204. Result := MillisecondsToString(aMilliseconds.ToExtended,pfTruncate,LongFormat);
  205. end;
  206. class function TChronometer.Precission(aValue : Extended; FormatPrecission : TPrecissionFormat) : Extended;
  207. begin
  208. case FormatPrecission of
  209. pfRound : Result := Round(aValue).ToExtended;
  210. pfTruncate : Result := Int(aValue);
  211. else Result := aValue;
  212. end;
  213. end;
  214. class function TChronometer.MillisecondsToString(aMilliseconds : Extended; FormatPrecission : TPrecissionFormat = pfFloat; LongFormat : Boolean = False) : string;
  215. var
  216. dt : TDateTime;
  217. mc : Extended;
  218. begin
  219. if aMilliseconds < 1.0 then
  220. begin
  221. mc := frac(aMilliseconds) * 1000;
  222. if Int(mc) = 0 then Result := Format('%d%s',[Trunc(frac(mc) * 1000),GetUnitTime(utNanosecond,LongFormat)]) //nanoseconds
  223. else Result := Format('%d%s',[Trunc(mc),GetUnitTime(utMicrosecond,LongFormat)]); //microseconds
  224. end
  225. else
  226. begin
  227. if aMilliseconds < MSecsPerSec then //milliseconds
  228. begin
  229. aMilliseconds := Precission(aMilliseconds,FormatPrecission);
  230. if (FormatPrecission = pfFloat) or (frac(aMilliseconds) > 0) then Result := Format('%f%s',[aMilliseconds,GetUnitTime(utMillisecond,LongFormat)])
  231. else Result := Format('%d%s',[Trunc(aMilliseconds),GetUnitTime(utMillisecond,LongFormat)])
  232. end
  233. else if (aMilliseconds / MSecsPerSec) < SecsPerMin then //seconds
  234. begin
  235. aMilliseconds := Precission((aMilliseconds / MSecsPerSec),FormatPrecission);
  236. if (FormatPrecission = pfFloat) or (frac(aMilliseconds) > 0) then Result := Format('%f%s',[aMilliseconds,GetUnitTime(utSecond,LongFormat)])
  237. else Result := Format('%d%s',[Trunc(aMilliseconds),GetUnitTime(utSecond,LongFormat)]);
  238. end
  239. else if ((aMilliseconds / MSecsPerSec) < SecsPerHour) and ((Round(aMilliseconds) mod (SecsPerMin * MSecsPerSec)) = 0) then //minutes
  240. begin
  241. aMilliseconds := Precission((aMilliseconds / (SecsPerMin * MSecsPerSec)),FormatPrecission);
  242. if (FormatPrecission = pfFloat) or (frac(aMilliseconds) > 0) then Result := Format('%f%s',[aMilliseconds,GetUnitTime(utMinute,LongFormat)])
  243. else Result := Format('%d%s',[Trunc(aMilliseconds),GetUnitTime(utMinute,LongFormat)])
  244. end
  245. else if (aMilliseconds / MSecsPerSec) < SecsPerDay then //hours
  246. begin
  247. dt := aMilliseconds / MSecsPerSec / SecsPerDay;
  248. if LongFormat then
  249. begin
  250. if (aMilliseconds / MSecsPerSec) > SecsPerHour then Result := FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt))
  251. else Result := FormatDateTime(GetFmtTime(tfMinutesAndSeconds,LongFormat),Frac(dt));
  252. end
  253. else
  254. begin
  255. Result := FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt));
  256. end;
  257. end
  258. else //días
  259. begin
  260. dt := aMilliseconds / MSecsPerSec / SecsPerDay;
  261. Result := Format('%d%s, %s', [trunc(dt),GetUnitTime(utDay,LongFormat),FormatDateTime(GetFmtTime(tfHoursAndMinutes,LongFormat),Frac(dt))]);
  262. end;
  263. end;
  264. end;
  265. function TChronometer.GetElapsedMilliseconds : TLargeInteger;
  266. begin
  267. result := (MSecsPerSec * (fStopCount - fStartCount)) div fFrequency;
  268. end;
  269. function TChronometer.GetElapsedMilliseconds_BreakPoint : TLargeInteger;
  270. begin
  271. result := (MSecsPerSec * (fStopBreakPoint - fStartBreakPoint)) div fFrequency;
  272. end;
  273. function TChronometer.GetElapsedMillisecondsWithPrecission : Extended;
  274. begin
  275. result := (MSecsPerSec * (fStopCount - fStartCount)) / fFrequency;
  276. end;
  277. function TChronometer.GetElapsedMillisecondsWithPrecission_BreakPoint : Extended;
  278. begin
  279. result := (MSecsPerSec * (fStopBreakPoint - fStartBreakPoint)) / fFrequency;
  280. end;
  281. function TChronometer.GetElapsedSeconds : TLargeInteger;
  282. begin
  283. result := ((MSecsPerSec * (fStopCount - fStartCount)) div fFrequency) div MSecsPerSec;
  284. end;
  285. procedure TChronometer.Start;
  286. begin
  287. SetTickStamp(fStartCount);
  288. fIsRunning := true;
  289. end;
  290. procedure TChronometer.Stop;
  291. begin
  292. SetTickStamp(fStopCount);
  293. fIsRunning := false;
  294. end;
  295. procedure TChronometer.Reset;
  296. begin
  297. SetTickStamp(fStartCount);
  298. end;
  299. procedure TChronometer.Check;
  300. begin
  301. if fIsRunning then SetTickStamp(fStopCount);
  302. end;
  303. procedure TChronometer.BreakPoint;
  304. begin
  305. if fIsRunning then
  306. begin
  307. if fStartBreakPoint = 0 then
  308. begin
  309. SetTickStamp(fStopBreakPoint);
  310. fStartBreakPoint := fStartCount;
  311. end
  312. else
  313. begin
  314. fStartBreakPoint := fStopBreakPoint;
  315. SetTickStamp(fStopBreakPoint);
  316. end;
  317. end
  318. else fStopBreakPoint := fStopCount;
  319. end;
  320. { TChronoBenchmark Class }
  321. constructor TChronoBenchmark.Create;
  322. begin
  323. inherited;
  324. fTotalProcess := 0;
  325. fSpeed := 0;
  326. fLastUpdateTime := Now();
  327. fCurrentProcess := 0;
  328. fFirstUpdateTime := 0;
  329. fEstimatedMilliseconds := 0;
  330. end;
  331. procedure TChronoBenchmark.SetCurrentProcess(NewCurrentProcess : Int64);
  332. begin
  333. //corrects first time run
  334. if fLastUpdateTime = 0 then fLastUpdateTime := Now();
  335. if fFirstUpdateTime = 0 then fFirstUpdateTime := Now();
  336. //calculates operation speed
  337. fSpeed := (NewCurrentProcess - fCurrentProcess) / ((Now() - fLastUpdateTime) * 86400);
  338. if fSpeed = 0 then fSpeed := 0.1;
  339. //returns estimated time string
  340. fEstimatedMilliseconds := Round(((TotalProcess - NewCurrentProcess) / fSpeed) * 1000);
  341. //save current values
  342. fLastUpdateTime := Now();
  343. fCurrentProcess := NewCurrentProcess;
  344. end;
  345. function TChronoBenchmark.GetElapsedMilliseconds : TLargeInteger;
  346. begin
  347. Result := Round(((Now() - fFirstUpdateTime) * 86400 * 1000));
  348. end;
  349. function TChronoBenchmark.ElapsedTime(LongFormat : Boolean) : string;
  350. begin
  351. Result := TChronometer.MillisecondsToString(GetElapsedMilliseconds,LongFormat);
  352. end;
  353. function TChronoBenchmark.EstimatedTime(LongFormat : Boolean) : string;
  354. begin
  355. Result := TChronometer.MillisecondsToString(fEstimatedMilliseconds,LongFormat);
  356. end;
  357. procedure TChronoBenchmark.Reset;
  358. begin
  359. fLastUpdateTime := Now();
  360. fSpeed := 0;
  361. fCurrentProcess := 0;
  362. fFirstUpdateTime := 0;
  363. fEstimatedMilliseconds := 0;
  364. end;
  365. end.