Quick.Chrono.pas 13 KB

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