Home Simple Code Profiling In Delphi
Post
Cancel

Simple Code Profiling In Delphi

I imagine most Delphi developers have written code similar to the following to quickly test a block of code using the Now function:

1
2
3
4
5
6
7
8
9
10
procedure TDemoExecutionProfilerForm.DoSomeComplexCode;
var
  StartTime:TDateTime;
  DebugMessage:String;
begin
  StartTime := Now;
  Sleep(75);  //some time intensive code block
  DebugMessage := Format('DoSomeComplexCode took %dms', [MillisecondsBetween(Now, StartTime)]);
  OutputDebugString(PChar(DebugMessage));
end;

This type of code is quick and easy to implement and it basically works for testing the occasional block of code. Newer versions of Delphi introduced TStopWatch for high-performance timings that is typically now used instead of the ‘Now’ type code above. Alternatively, you can rely on a few full-blown code profiler tools for Delphi to examine your entire project. The most common Delphi profilers currently are (please let me know if I missed your favorite):

Besides the choice of commercial vs. open source, the main choice of which profiler to use boils down to using a “sampling” or “instrumenting” profiler, and you should visit the link above for Eric Grange’s website for a nice breakdown between the two.

I was recently doing some performance analysis and revisited most of the tools above and I purchased ProDelphi as I have seen the product for many years but I have never tried it. To be honest, the website hasn’t changed much in probably 15+ years so I never trusted it enough to make the purchase, even though it is relatively inexpensive. The purchase ended up being quick and efficient, and the product does work as expected.

Profiling Code In Delphi

After finding the area of code that needed some performance improvements, I implemented a slightly-better version of the simple “Now”-type code above to easily profile blocks of code without using an external tool. The replacement code ends up looking like the snippet below:

1
2
3
4
5
6
procedure TDemoExecutionProfilerForm.DoSomeComplexCode;
begin
  TiaProfiler.StartTimer('DoSomeComplexCode');
  Sleep(75);  //some time intensive code block
  TiaProfiler.StopTimer('DoSomeComplexCode');
end;

If you sprinkle some of these profiling lines around the project, you can easily profile sections of code. Additionally, if you add a small block of code when your application terminates, you can save performance history to disk for comparisons over time. A simple example of saving performance results to a file is shown in the code snippet below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
procedure TDemoExecutionProfilerForm.FormDestroy(Sender: TObject);
var
  StatsList:TStringList;
begin
  StatsList := TStringList.Create;
  try
    if TiaProfiler.ExportStats(StatsList, TiaExportResolution.Milliseconds) > 0 then
    begin
      StatsList.SaveToFile('PerformanceStats.csv');
    end;
  finally
    StatsList.Free;
  end;
end;

The performance stats exported for each named timer include Total Executions, Total Elapsed Time, Average Time, Min Time, and Max Time. The time resolution can be specified as Ticks or Milliseconds.

Besides the basic Stop/Stop timing methods, a few small utility methods included are ClearTimers and direct access to a TimersAreEnabled property to disable/enable timers on demand. You may want to use these to concentrate on a particular section of code by calling ClearTimers before a method is called and then disable any future timers afterwards to focus only on the timers related to a small section of code. In addition, the Start, Stop, and ExportStats calls are protected by a critical section for multi-threaded access.

Ideally, you would also surround all the profiling code added with a compiler define so that it is only enabled for certain builds. The simple example above has been modified so that the profiling code is conditionally executed in the following snippet:

1
2
3
4
5
6
procedure TDemoExecutionProfilerForm.DoSomeComplexCode;
begin
  {$IFDEF PROFILER}TiaProfiler.StartTimer('DoSomeComplexCode');{$ENDIF}
  Sleep(75);  //some time intensive code block
  {$IFDEF PROFILER}TiaProfiler.StopTimer('DoSomeComplexCode');{$ENDIF}
end;

The profiling code is available on GitHub in the small iaLib shared code repository that has been used for other posts on this blog. A simple demo project is included to get started. It is a small step above the “Now” type debugging code at the start of the blog post but does not replace the need for one of the profiler tools mentioned above. If you want a commercial profiler, then I would recommend the ProDelphi tool for cost and features. If you want an open source tool, then the Sampling Profiler by Eric Grange is definitely one to consider.