Skip to content
Permalink
Browse files

refactored TPrecisionTimer - especially fixing Pause/Resume process

  • Loading branch information
Arnaud Bouchez
Arnaud Bouchez committed Feb 13, 2020
1 parent 25120d7 commit 30815ab00045b0a81cf6f3feb67698c422c89043
Showing with 80 additions and 118 deletions.
  1. +0 −1 SQLite3/mORMotDDD.pas
  2. +1 −3 SQLite3/mORMotSQLite3.pas
  3. +66 −99 SynCommons.pas
  4. +3 −3 SynDBOracle.pas
  5. +2 −1 SynSQLite3.pas
  6. +7 −10 SynSelfTests.pas
  7. +1 −1 SynopseCommit.inc
@@ -2190,7 +2190,6 @@ constructor TDDDMonitoredDaemon.Create(aRest: TSQLRest);
begin
fProcessIdleDelay := 50;
fProcessLock := TAutoLocker.Create;
fProcessTimer.Start;
if fProcessThreadCount<1 then
fProcessThreadCount := 1 else
if fProcessThreadCount>20 then
@@ -820,10 +820,8 @@ procedure TSQLRestServerDB.GetAndPrepareStatementRelease(E: Exception;
try
if fStatementTimer<>nil then begin
if fStatementMonitor<>nil then
fStatementMonitor.ProcessEnd else begin
fStatementMonitor.ProcessEnd else
fStatementTimer^.Pause;
fStatementTimer^.ComputeTime;
end;
if E=nil then
if (fStatementTruncateSQLLogLen > 0) and
(length(fStatementSQL) > fStatementTruncateSQLLogLen) then begin
// want to alllocate a local timer instance on the stack
{$ifdef FPC_OR_UNICODE}TPrecisionTimer = record private
{$else}TPrecisionTimer = object protected{$endif}
fStart,fStop,fResume,fLast: Int64;
fStart,fStop: Int64;
{$ifndef LINUX} // use QueryPerformanceMicroSeconds() fast API
fWinFreq: Int64;
{$endif}
fPauseCount: TSynMonitorCount;
public
/// initialize the timer
// - not necessary if created on the heap (e.g. as class member)
// - will set all fields to 0
procedure Init;
// - will fill all internal state with 0
// - not necessary e.g. if TPrecisionTimer is defined as a TObject field
procedure Init; {$ifdef HASINLINE}inline;{$endif}
/// initialize and start the high resolution timer
// - similar to Init + Resume
procedure Start;
/// returns TRUE if fStart is not 0
function Started: boolean; {$ifdef HASINLINE}inline;{$endif}
/// stop the timer, setting the Time elapsed since last Start
procedure ComputeTime; {$ifdef LINUX}{$ifdef HASINLINE}inline;{$endif}{$endif}
/// stop the timer, returning the total time elapsed as text
// - with appened time resolution (us,ms,s) - from MicroSecToString()
// - is just a wrapper around ComputeTime + Time
function Stop: TShort16;
// - with appended time resolution (us,ms,s) - from MicroSecToString()
// - is just a wrapper around Pause + Time
// - you can call Resume to continue adding time to this timer
function Stop: TShort16; {$ifdef HASINLINE}inline;{$endif}
/// stop the timer, ready to continue its time measurement via Resume
// - will also compute the global Time value
// - do nothing if no previous Start/Resume call is pending
procedure Pause;
/// resume a paused timer
// - if the previous method called was Pause, it will ignore all the
// time elapsed since then
// - if the previous method called was Start, it will start as if it was
// in pause mode
procedure Resume;
/// resume a paused timer, or start an initialized timer
// - do nothing if no timer has been initialized or paused just before
// - if the previous method called was Init, will act like Start
// - if the previous method called was Pause, it will continue counting
procedure Resume; {$ifdef HASINLINE}inline;{$endif}
/// resume a paused timer until the method ends
// - will internaly create a TInterfaceObject class to let the compiler
// generate a try..finally block as expected to call Pause at method ending
// - by default, this timer is not thread safe: you can use this method to
// set the timing values from manually computed performance counters
// - the caller should also use a mutex to prevent from race conditions:
// see e.g. TSynMonitor.FromExternalQueryPerformanceCounters implementation
// - returns the time elapsed, in micro seconds (i.e. LastTime value)
// see e.g. TSynMonitor.FromExternalMicroSeconds implementation
// - warning: Start, Stop, Pause and Resume methods are then disallowed
function FromExternalQueryPerformanceCounters(const CounterDiff: QWord): QWord;
procedure FromExternalMicroSeconds(const MicroSeconds: QWord);
{$ifdef FPC_OR_UNICODE}inline;{$endif} // Delphi 2007 is buggy as hell
/// low-level method to force values settings to allow thread safe timing
// - by default, this timer is not thread safe: you can use this method to
// set the timing values from manually computed performance counters
// - the caller should also use a mutex to prevent from race conditions:
// see e.g. TSynMonitor.FromExternalMicroSeconds implementation
// see e.g. TSynMonitor.FromExternalQueryPerformanceCounters implementation
// - returns the time elapsed, in micro seconds (i.e. LastTime value)
// - warning: Start, Stop, Pause and Resume methods are then disallowed
procedure FromExternalMicroSeconds(const MicroSeconds: QWord);
{$ifdef FPC_OR_UNICODE}inline;{$endif} // Delphi 2007 is buggy as hell
function FromExternalQueryPerformanceCounters(const CounterDiff: QWord): QWord;
{$ifdef FPCLINUX}inline;{$endif}
/// compute the per second count
function PerSec(const Count: QWord): QWord;
/// compute the time elapsed by count, with appened time resolution (us,ms,s)
function Stop: TShort16;
/// stop the timer, ready to continue its time measure
procedure Pause;
/// resume a paused timer
/// resume a paused timer, or start it if it hasn't be started
procedure Resume;
/// compute the per second count
function PerSec(Count: cardinal): cardinal;
function Stop: TShort16;
/// stop the timer, ready to continue its time measure
procedure Pause;
/// resume a paused timer
/// resume a paused timer, or start the timer
procedure Resume;
/// compute the per second count
function PerSec(Count: cardinal): cardinal;

{ TPrecisionTimer }

function TPrecisionTimer.ByCount(Count: QWord): TShort16;
begin
if Count=0 then
result := '0' else // avoid div per 0 exception
MicroSecToString(fTime div Count,result);
end;

function TPrecisionTimer.PerSec(const Count: QWord): QWord;
begin
if fTime<=0 then // avoid negative value in case of incorrect Start/Stop sequence
result := 0 else // avoid div per 0 exception
result := (Count*1000000) div fTime;
end;

function TPrecisionTimer.SizePerSec(Size: QWord): shortstring;
begin
FormatShort('% in % i.e. %/s',[KB(Size),Stop,KB(PerSec(Size))],result);
end;

{$ifdef FPC} {$push} {$endif} {$HINTS OFF} // avoid "loop executed zero times"
procedure TPrecisionTimer.Init;
begin
FillCharFast(self,SizeOf(self),0);
begin
FillCharFast(self,SizeOf(self),0);
{$ifdef LINUX}QueryPerformanceMicroSeconds{$else}QueryPerformanceCounter{$endif}(fStart);
fLast := fStart;
end;
{$ifdef FPC} {$pop} {$else} {$HINTS ON} {$endif}

function TPrecisionTimer.Started: boolean;
procedure TPrecisionTimer.Resume;
begin
result := fStart <> 0;
if fStart=0 then
{$ifdef LINUX}QueryPerformanceMicroSeconds{$else}QueryPerformanceCounter{$endif}(fStart);
end;

procedure TPrecisionTimer.ComputeTime;
procedure TPrecisionTimer.Pause;
begin
{$ifdef LINUX}
QueryPerformanceMicroSeconds(fStop);
fTime := fStop-fStart;
fLastTime := fStop-fLast;
{$else}
QueryPerformanceCounter(fStop);
if fWinFreq=0 then begin
QueryPerformanceFrequency(fWinFreq);
if fWinFreq=0 then begin
fTime := 0;
fLastTime := 0;
exit;
end;
end;
{$ifdef DELPHI5OROLDER} // circumvent C1093 Error
fTime := ((fStop-fStart)*1000000) div fWinFreq;
if fLast=fStart then
fLastTime := fTime else
fLastTime := ((fStop-fLast)*1000000) div fWinFreq;
{$else}
fTime := (QWord(fStop-fStart)*QWord(1000000)) div QWord(fWinFreq);
if fLast=fStart then
fLastTime := fTime else
fLastTime := (QWord(fStop-fLast)*QWord(1000000)) div QWord(fWinFreq);
{$endif DELPHI5OROLDER}
{$endif LINUX}
if fStart=0 then
exit;
{$ifdef LINUX}QueryPerformanceMicroSeconds{$else}QueryPerformanceCounter{$endif}(fStop);
FromExternalQueryPerformanceCounters(fStop-fStart);
inc(fPauseCount);
end;

procedure TPrecisionTimer.FromExternalMicroSeconds(const MicroSeconds: QWord);
begin
fLastTime := MicroSeconds;
inc(fTime,MicroSeconds);
fStart := 0; // indicates time has been computed
end;

function TPrecisionTimer.FromExternalQueryPerformanceCounters(const CounterDiff: QWord): QWord;
begin // mimics ComputeTime from already known elapsed time
begin // mimics Pause from already known elapsed time
{$ifdef LINUX}
FromExternalMicroSeconds(CounterDiff);
{$else}
if fWinFreq=0 then begin
fTime := 0;
fLastTime := 0;
if fWinFreq=0 then
QueryPerformanceFrequency(fWinFreq);
end;
if fWinFreq<>0 then
FromExternalMicroSeconds((CounterDiff*1000000)div PQWord(@fWinFreq)^);
{$endif LINUX}

function TPrecisionTimer.Stop: TShort16;
begin
ComputeTime;
if fStart<>0 then
Pause;
MicroSecToString(fTime,result);
end;

procedure TPrecisionTimer.Pause;
function TPrecisionTimer.Time: TShort16;
begin
{$ifdef LINUX}QueryPerformanceMicroSeconds{$else}QueryPerformanceCounter{$endif}(fResume);
dec(fResume,fStart);
inc(fPauseCount);
if fStart<>0 then
Pause;
MicroSecToString(fTime,result);
end;

procedure TPrecisionTimer.Resume;
function TPrecisionTimer.LastTime: TShort16;
begin
{$ifdef LINUX}QueryPerformanceMicroSeconds{$else}QueryPerformanceCounter{$endif}(fStart);
fLast := fStart;
dec(fStart,fResume);
fResume := 0;
if fStart<>0 then
Pause;
MicroSecToString(fLastTime,result);
end;

function TPrecisionTimer.Time: TShort16;
function TPrecisionTimer.ByCount(Count: QWord): TShort16;
begin
MicroSecToString(fTime,result);
if Count=0 then // avoid div per 0 exception
result := '0' else begin
if fStart<>0 then
Pause;
MicroSecToString(fTime div Count,result);
end;
end;

function TPrecisionTimer.LastTime: TShort16;
function TPrecisionTimer.PerSec(const Count: QWord): QWord;
begin
MicroSecToString(fLastTime,result);
if fStart<>0 then
Pause;
if fTime<=0 then // avoid negative value in case of incorrect Start/Stop sequence
result := 0 else // avoid div per 0 exception
result := (Count*1000000) div fTime;
end;

function TPrecisionTimer.SizePerSec(Size: QWord): shortstring;
begin
FormatShort('% in % i.e. %/s',[KB(Size),Stop,KB(PerSec(Size))],result);
end;



function TPrecisionTimer.ProfileCurrentMethod: IUnknown;
begin
if fStart=0 then
Start else
Resume;
Resume;
result := TPrecisionTimerProfiler.Create(@self);
end;

fSafe^.Lock;
try
InternalTimer.Pause;
InternalTimer.ComputeTime;
LockedFromProcessTimer;
finally
fSafe^.UnLock;
@@ -2519,7 +2519,7 @@ constructor TSQLDBOracleStatement.CreateFromExistingStatement(
aConnection: TSQLDBConnection; aStatement: POCIStmt);
begin
Create(aConnection);
with fTimeElapsed do if Started then Resume else Start;
fTimeElapsed.Resume;
try
fStatement := aStatement;
try
@@ -2664,7 +2664,7 @@ procedure TSQLDBOracleStatement.ExecutePrepared;
tmp := SQLWithInlinedParams;
Log(sllSQL,tmp,self,2048);
end;
with fTimeElapsed do if Started then Resume else Start;
fTimeElapsed.Resume;
try
ociArraysCount := 0;
Env := (Connection as TSQLDBOracleConnection).fEnv;
@@ -3354,7 +3354,7 @@ procedure TSQLDBOracleStatement.Prepare(const aSQL: RawUTF8;
var oSQL: RawUTF8;
env: POCIEnv;
begin
with fTimeElapsed do if Started then Resume else Start;
fTimeElapsed.Resume;
try
try
if (fStatement<>nil) or (fColumnCount>0) then
@@ -2107,6 +2107,7 @@ TSQLite3Library = class
property VersionText: RawUTF8 read fVersionText;
published
/// will return the class name and SQLite3 version number
// - if self (e.g. global sqlite3) is nil, will return ''
property Version: RawUTF8 read GetVersion;
end;
{$M-}
@@ -5961,7 +5962,7 @@ function TSQLite3Library.GetVersion: RawUTF8;
begin
if self=nil then
result := 'No TSQLite3Library available' else
FormatUTF8('% with %ternal MM',[fVersionText,MM[fUseInternalMM]],result);
FormatUTF8('% % with %ternal MM',[self,fVersionText,MM[fUseInternalMM]],result);
end;


0 comments on commit 30815ab

Please sign in to comment.
You can’t perform that action at this time.