Don’t abuse FreeAndNil anymore

February 6th, 2010

A recurring subject when it comes to freeing objects and preventing is whether you should just .Free them, thus leaving a invalid reference that should however never be used anymore when the code design is correct, or if you should defensively FreeAndNil() them, thus leaving a nil value that will hopefully trigger AVs more often on improper usage after release.

Allen Bauer recently brought this subject in his blog “A case against FreeAndNil“, arguing that there are better tools than FreeAndNil to diagnose improper usage after release, and that it can hide other issues and lead to other magic bullet solutions, which only further the problem. This is true, and FastMM debug mode can do wonders here, however, quite often, you don’t want to rely on a debug and diagnostic machinery that needs to be switched ON for problems to be detected early on.

Well, if you’re using FreeAndNil() for defensive purposes, don’t abuse it anymore, invest in a few lines of code for a shiny new FreeAndInvalidate():

procedure FreeAndInvalidate(var obj);
var
   temp : TObject;
begin
   temp := TObject(obj);
   Pointer(obj) := Pointer(1);
   temp.Free;
end;

This function frees the object and sets the reference to an invalid magic value, which will trigger and AV on improper field or virtual method access after release  (just like FreeAndNil), but unlike FreeAndNil, it will also AV on multiple .Free attempt, and will not be stopped by “if Assigned()” checks. If you wish even more defense, you can also “sabotage” the VMT pointer of the freed object instance.

With a FreeAndInvalidate() added to your bag of tricks, you can now reserve FreeAndNil usage to situations where having a nil reference is truly part of the design, and no longer abuse it for defensive programming. Of course this is still no magic-bullet, but it’s cheap enough that you can use it in release builds (unlike debug and diagnostic tools), and as a bonus, it makes it obvious when reading the code that the object reference is supposed to be invalid after the call.

Eric Tips ,

SamplingProfiler v1.7.4

September 8th, 2009

SamplingProfiler v1.7.4 is now available. This version adds an option for Delphi 2010 paths, and fixes a bug with the silent mode execution that would render it inoperative. There also have been other minor changes, mostly cosmetic.

This release also includes preparation for an “attach to process” option, which is currently not enabled, but should hopefully make in the next version (available “when ready”).

Eric News , , ,

ZJDBGPack 2.0 improved “batch-ability”

July 10th, 2009

An improved version of ZJDBGPack has been released, with better error messages and non-zero exit codes when an error occurs. This makes it more usable for batches and automated builds.

Eric News ,

Profiling multi-threaded applications

May 27th, 2009

SamplingProfiler has a few options to help profile a multi-threaded application which I’ll go over here.

In the current version, those options allow identifying CPU-related bottlenecks, as in “threads taking too much CPU resources or execution time”. However, they do not provide much clues yet to pinpoint bottlenecks arising from thread synchronization issues or serialization (insufficient parallelism). Hopefully, more support for profiling multi-threaded applications will come in future versions.

Single-threaded profiling

By default, SamplingProfiler only looks at one thread, the main application thread, but you can manually (and dynamically) specify another thread. This is done via OutputDebugString (see Control sampling from your code)

OutputDebugString('SAMPLING THREAD threadID');

with threadID the thread ID (as returned from the WinAPI function GetCurrentThreadID f.i.). If you specify an invalid threadID, or if the thread dies, no more samples will be collected until you specify a new thread or “return” the sampling focus to the main thread, which can be accomplished with

OutputDebugString('SAMPLING THREAD 0');

This command is mostly useful if you already have a clue which thread is proving troublesome, like when a worker thread is used in GUI interface. If you have several worker threads in a thread pool, which serve random workloads (or assumed random),  you can pick one of those threads (at random) and have it profiled.

However, this involves a fair amount of bias and guessing where the bottleneck could be, and is not really applicable if you have a high number of threads working (or sleeping) simultaneously on multiple CPUs. This is where comes in…

Monte-Carlo Samples Gathering

Monte-Carlo sampling is specified via the samples gathering mode option, when set, SamplingProfiler will pick a random thread of the profiled application at each sampling, and use it for the sample. Bias and guessing are eliminated.

The good news is that with this method, the sampling load is not increased, and its impact is random: concurrency issues and UI bottlenecks can still be spotted. Hot-spots in a server running at production speed can be spotted too.

The bad news is that if you have a high number of inactive threads, you’ll have to gather more samples to get meaningful results on the active threads (as each time an inactive thread is picked at random, the sample will be meaningless, and thus lost).

Interpreting the profiling results can however be a little more difficult, as several multi-threading effects can come into play, for instance a drop in CPU cache efficiency (code stressed in highly threaded situations can behave quite differently from what it looks when stressed in single-threaded situation). This will be food for future articles.

To decide if a thread is active or not, SamplingProfiler looks at its registers: if all the registers are unchanged between two samplings, the thread is deemed inactive and the sample dropped. Inactivity can thus result from the thread being sleeping or waiting on some event, or just from having not gotten its share of CPU time since the last time it was sampled (this can be quite common if you have a much higher number of threads than you have CPU cores, even if all the threads are busy).

CPU Affinity

The last set of options is the one for processor affinities. You can choose on which CPUs SamplingProfiler is constrained, and on which CPUs the profiled application is constrained.

Affinities can be used either to further isolate the profiled application from the profiler, or to easily simulate your application running on a machine with less cores. In more advanced scenarios, if you have enough CPU cores, you can also leave CPU cores entirely unused by both the profiler or the profiled, and thus reserve them to a third application (such as a database server).

Eric Tips , , , , , ,

SamplingProfiler v1.7.3 bug fix

May 22nd, 2009

SamplingProfiler v1.7.3 has now been released and should be used in place of 1.7.2 which was pulled.

1.7.2 had a nasty bug in the timings statistics (promptly spotted by Robert Houdart) which should be fixed in 1.7.3, there are no other changes and additions in this version.

Eric News , ,