Work and processing classes are typically short-lived, created to perform one form of processing or another then freed. They can be simple collections, handle I/O of one kind of another, perform computations, pattern matching, etc.
When they’re used for simple workloads, their short-lived temporary nature can sometimes become a performance problem.
The typical code for those is
worker := TWorkClass.Create; try ...do some work.. finally worker.Free; end;
This code can end up bottle-necking on the allocation/de-allocation when
- you do that a whole lot of times, and from various contexts
- you do that in a multi-threaded application and hit the single-threaded memory allocation limits
Spotting the Issue in the Profiler
If you use Sampling Profiler, you’ll know this because your profilling results wil look like this
with TObject.InitInstance, @AfterConstruction, CleanupInstance, etc. up there, you just know you’re hitting the RTL object allocation/de-allocation overhead.
In the case of a single-threaded application, the RTL will often be the bottleneck, the memory manager is there but not anywhere critical. For more complex classes with managed fields, it will involve even more RTL time, and that will be in addition to your own Create/Destroy code.
In a multi-threaded application, you may see SysGetMem/SysFreeMem creep slowly toward the top spots.
Context Instance and Object Pools
A simple “fix” would be to create the work class once, then pass it around directly or in a “context” class (ie. together with other work classes). However, that isn’t always practical, either because the “context” isn’t well defined, or it would expose implementation details, or you just don’t to want to pass around a context parameter that would act as spaghetti-glue across libraries.
Another alternative is to use a full-blown object pool, but that usually involves a collection, and if you want to be thread-safe, it means some form of locking or a more complex lock-free collection, all kind of things which may not just be overkill, but could leave you with a complex pool that doesn’t behave much better than the original code for simple work classes.
Enter the mini object-pool, which is no panacea, but is really mini, thread-safe and might just be enough to take care of your allocation/de-allocation problem.