BDE 4.14.0 Production release
|
Provide a memory manager that manages an external buffer.
This component provides a memory manager ("buffer manager"), bdlma::BufferManager
, that dispenses heterogeneous memory blocks (of varying, user-specified sizes) from an external buffer. A BufferManager
has a similar interface to a sequential pool in that the two methods allocate
and release
are provided.
In addition to the allocate
method, a less safe but faster variation, allocateRaw
, is provided to support memory allocation: If there is insufficient memory remaining in the buffer to satisfy an allocation request, allocate
will return 0 while allocateRaw
will result in undefined behavior.
The behavior of allocate
and allocateRaw
illustrates the main difference between this buffer manager and a sequential pool. Once the external buffer runs out of memory, the buffer manager does not self-replenish, whereas a sequential pool will do so.
The release
method resets the buffer manager such that the memory within the entire external buffer will be made available for subsequent allocations. Note that individually allocated memory blocks cannot be separately deallocated.
bdlma::BufferManager
is typically used for fast and efficient memory allocation, when the user knows in advance the maximum amount of memory needed.
This section illustrates intended use of this component.
Suppose that we need to detect whether there are at least n
duplicates within an array of integers. Furthermore, suppose that speed is a concern and we need the fastest possible implementation. A natural solution will be to use a hash table. To further optimize for speed, we can use a custom memory manager, such as bdlma::BufferManager
, to speed up memory allocations.
First, let's define the structure of a node inside our custom hash table structure:
Note that sizeof(my_Node) == 12
when compiled in 32-bit mode, and sizeof(my_Node) == 16
when compiled in 64-bit mode. This difference affects the amount of memory used under different alignment strategies (see bsls_alignment for more details on alignment strategies).
We can then define the structure of our specialized hash table used for integer counting:
The implementation of the rest of my_IntegerCountingHashTable
is elided as the class method calculateBufferSize
, constructor, and the insert
method alone are sufficient to illustrate the use of bdlma::BufferManager
:
Note that, in case the allocated buffer is not aligned, the size calculation includes a "fudge" factor equivalent to the maximum alignment requirement of the platform.
Note that bdlma::BufferManager
is used to allocate memory blocks of heterogeneous sizes. In the constructor, memory is allocated for the node array. In insert
, memory is allocated for the nodes.
Finally, in the following detectNOccurrences
function, we can use the hash table class to detect whether any integer value occurs at least n
times within a specified array:
We then allocate an external buffer to be used by bdlma::BufferManager
. Normally, this buffer will be created on the program stack if we know the length in advance (for example, if we specify in the contract of this function that we only handle arrays having a length of up to 10,000 integers). However, to make this function more general, we decide to allocate the memory dynamically. This approach is still much more efficient than using the default allocator, say, to allocate memory for individual nodes within insert
, since we need only a single dynamic allocation, versus separate dynamic allocations for every single node:
We use a bslma::DeallocatorGuard
to automatically deallocate the buffer when the function ends:
Note that the calculation of MAX_SIZE
assumes natural alignment. If maximum alignment is used instead, a larger buffer is needed since each node object will then be maximally aligned, which takes up 16 bytes each instead of 12 bytes on a 32-bit architecture. On a 64-bit architecture, there will be no savings using natural alignment since the size of a node will be 16 bytes regardless.