Here's a tiny interfaced class I wrote because I'm chasing performance issues in a big GUI app, so I spend a lot of time going "that takes a while, I wonder which bit takes the time". Which means some sort of logging, and after my coworkers decided that SynLog was too hard I came up with this trivial thing:
Here's a tiny interfaced class I wrote because I'm chasing performance issues in a big GUI app, so I spend a lot of time going "that takes a while, I wonder which bit takes the time". Which means some sort of logging, and after my coworkers decided that SynLog was too hard I came up with this trivial thing:
type
ILogTimer = interface
end;
TLogTimer = class(TInterfacedObject, ILogTimer)
private
FStartTime: TDateTime;
FCaption: string;
public
constructor Create(const Caption: string); reintroduce;
destructor Destroy; override;
end;
constructor TLogTimer.Create(const Caption: string);
begin
inherited Create;
FStartTime := Now;
FCaption := Caption;
end;
destructor TLogTimer.Destroy;
begin
OutputDebugString(PChar(Format('%s %0.1gms', [FCaption, (Now -
FStartTime) * SecsPerday * 1000])));
inherited;
end;
Usage is two lines, the ILogTimer variable and a constructor call to assign it. The idea is that it's less typing every time I want to time something, and if I need to I can stop the whole lot in one place (by undefining something, say)
type
ILogTimer = interface
end;
TLogTimer = class(TInterfacedObject, ILogTimer)
private
FStartTime: TDateTime;
FCaption: string;
public
constructor Create(const Caption: string); reintroduce;
destructor Destroy; override;
end;
constructor TLogTimer.Create(const Caption: string);
begin
inherited Create;
FStartTime := Now;
FCaption := Caption;
end;
destructor TLogTimer.Destroy;
begin
OutputDebugString(PChar(Format('%s %0.1gms', [FCaption, (Now -
FStartTime) * SecsPerday * 1000])));
inherited;
end;
Usage is two lines, the ILogTimer variable and a constructor call to assign it. The idea is that it's less typing every time I want to time something, and if I need to I can stop the whole lot in one place (by undefining something, say)
Moz Le, I agree, but the Delphi TStopWatch is an advanced record, so no try/finally/free is needed. In Delphi-7 the class implementations is the only one that works, but I'm sure that one could be turned into an interface solution as well.
ReplyDeleteNice idea, I was just thinking the other day that I should look into which parts of the code were waiting for queries, in order to improve responsiveness. This seems like a neat way.
ReplyDeleteI think this can be slightly improved using the fact that interfaces aren't freed until the end of the procedure/function:
Then you just have to do like this:
procedure TForm1.Button1Click(Sender: TObject);
begin
LogTimer('Button1Click');
Sleep(124);
end;
where
function LogTimer(const Caption: string): ILogTimer;
begin
result := TLogTimer.Create(Caption);
end;
I'd also record the start and end time and subtract, but that's a detail.
For reference here's the full code of my variant:
type
ILogTimer = interface
['{6A92AB8A-E144-4F9F-BD6A-50842401B539}']
end;
TLogTimer = class(TInterfacedObject, ILogTimer)
private
FStartTime: int64;
FCaption: string;
public
constructor Create(const Caption: string);
destructor Destroy; override;
end;
constructor TLogTimer.Create(const Caption: string);
begin
inherited Create;
QueryPerformanceCounter(FStartTime);
FCaption := Caption;
end;
destructor TLogTimer.Destroy;
var
finishTime, freq: int64;
begin
QueryPerformanceCounter(finishTime);
QueryPerformanceFrequency(freq);
OutputDebugString(PChar(Format('%s %0.1gms', [FCaption, (finishTime - FStartTime) * (1000.0 / freq)])));
inherited;
end;
function LogTimer(const Caption: string): ILogTimer;
begin
result := TLogTimer.Create(Caption);
end;
If you add a factory method returning an instance of the interface type you can use it without an ILogTimer variable. Call the factory method and don't assign the result to a variable. When the method exits it will release the instance then.
ReplyDeleteThis is probably a case of relying on an implementation detail. and it can be confusing to anyone who doesn't know why it works. Can be handy for simple things though, I have an ICursorChanger that sets the screen cursor to a wait cursor on creation and back to arrow on destruction, and a factory function BusyCursor. For methods that will block the UI thread for a moment I can just put a call to BusyCursor at the top of the method. When it scopes out at the end it resets the cursor.