C++ has been on my mind lately. There are many reasons why embedded developers are wary – possibly even afraid – of C++. These include code bloat, execution performance and unreliability resulting from dynamic memory allocation. They are all issues with which engineers should concern themselves, however, none of them are intrinsic problems with the C++ language, as they can all be controlled and contained.
The last on this list of concerns – dynamic memory allocation – I find particularly interesting …
In broad terms, I would say that dynamic allocation of memory in a real time system is a very bad idea. This is for two key reasons, both associated with the standard allocation function malloc(). First, an allocation call is non-deterministic – never good news when predictability of performance is a key requirement. Second, an allocation request may fail in a somewhat unpredictable manner. This is because of heap fragmentation – there is plenty of free memory available, but no contiguous block is large enough for a given request. In this example, there is 6K of free memory, but a request for a block larger than 3K will fail.
Of course, there is a solution. Just about every real time operating system [RTOS] around provides facilities for the deterministic allocation of fixed size memory blocks. You simply set up a “pool” of blocks during initialization and request and release blocks as required. The allocation time is predictable and the failure mode well defined and controlled. Of course, fixed size blocks sound restricting, but that is not the case for many types of application. If you really want to have something that looks like malloc(), you can easily write one. All that is needed is a set of memory pools with exponentially increasing block size. The new allocation function simply requests a large enough block from the appropriate pool.
In C++, the new operator essentially hides a call to malloc(), so rewriting this function addresses the issue. Another approach [which I was reminded about this week by Peter Bushell – thanks Peter] can be more efficient. When you instantiate a C++ class [for struct], the new operator is used to allocate space for its data. When you define a class, you can overload new to make it behave in a class-specific way. A good approach is to define a memory pool for each class, with blocks of precisely the required size. The size of the pools [i.e. number of blocks] depends on the usage of the class, but can be determined quite straightforwardly.