Quick.Chrono.pas 15 KB

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