BDE 4.14.0 Production release
|
Provide an efficient managed allocator using an external buffer.
This component provides a concrete mechanism, bdlma::BufferedSequentialAllocator
, that implements the bdlma::ManagedAllocator
protocol to very efficiently allocate heterogeneous memory blocks (of varying, user-specified sizes) from an external buffer supplied at construction:
If an allocation request exceeds the remaining free memory space in the external buffer, the allocator will fall back to a sequence of dynamically-allocated buffers. Users can optionally specify a growth strategy at construction that governs the growth rate of the dynamically-allocated buffers. If no growth strategy is specified at construction, geometric growth is used. Users can also optionally specify an alignment strategy at construction that governs the alignment of allocated memory blocks. If no alignment strategy is specified at construction, natural alignment is used. The release
method releases all memory allocated through the allocator, as does the destructor. The rewind
method releases all memory allocated through the allocator and returns to the underlying allocator only memory that was allocated outside of the typical internal buffer growth of the allocator (i.e., large blocks). Note that individually allocated memory blocks cannot be separately deallocated.
bdlma::BufferedSequentialAllocator
is typically used when users have a reasonable estimation of the amount of memory needed. This amount of memory would typically be created directly on the program stack, and used as the initial external buffer of the allocator for very fast memory allocation. While the buffer has sufficient capacity, memory allocations using the pool will not trigger any dynamic memory allocation, will have optimal locality of reference, and will not require deallocation upon destruction.
Once the external buffer is exhausted, subsequent allocation requests require dynamic memory allocation, and the performance of the allocator degrades.
The main difference between a bdlma::BufferedSequentialAllocator
and a bdlma::BufferedSequentialPool
is that, very often, the allocator is maintained through a bslma::Allocator
pointer - hence, every call to allocate
is a virtual function call, which is slower than invoking allocate
with the pool directly. However, the allocator interface is much more widely accepted across objects, and hence more general purpose.
An optional maxBufferSize
parameter can be supplied at construction to specify the maximum size (in bytes) of the dynamically-allocated buffers for geometric growth. Once the internal buffer grows up to the maxBufferSize
, further requests that exceed this size will be served by a separate memory block instead of the internal buffer. The behavior is undefined unless size <= maxBufferSize
, where size
is the extent (in bytes) of the external buffer supplied at construction.
Note that, even when a buffer having n
bytes of memory is supplied at construction, it does not mean that n
bytes of memory are available before dynamic memory allocation is triggered. This is due to memory alignment requirements. If the buffer supplied is not aligned, the first call to the allocate
method will automatically skip one or more bytes such that the memory allocated is properly aligned. The number of bytes that are wasted depends on whether natural alignment, maximum alignment, or 1-byte alignment is used (see bsls_alignment for more details).
This section illustrates intended use of this component.
Suppose we need to implement a method, calculate
, that performs calculations (where the specifics are not important to illustrate the use of this component), which require three vectors of double
values. Furthermore, suppose we know that we need to store at most 100 values for each vector:
Since the amount of memory needed is known in advance, we can optimize the memory allocation by using a bdlma::BufferedSequentialAllocator
to supply memory for the vectors. We can also prevent the vectors from resizing (which triggers more allocations) by reserving for the specific capacity we need:
In the above calculation, we assume that the only memory allocation requested by the vector is the allocation for the array that stores the double
values. Furthermore, we assume that the reserve
method allocates the exact amount of memory for the number of items specified (in this case, of type double
). Note that both of these assumptions are true for BDE's implementation of bsl::vector
.
To avoid alignment issues described in the "Warning" section (above), we create a bsls::AlignedBuffer
:
By making use of a bdlma::BufferedSequentialAllocator
, all dynamic memory allocation is eliminated in the above example.
Suppose we are receiving updates for price quotes for a list of securities through the following function:
Furthermore, suppose the number of securities we are interested in is limited. We can then use a bdlma::BufferedSequentialAllocator
to optimize memory allocation for the bsl::map
. We first create a buffer on the stack:
The calculation of the amount of memory needed is just an estimate, as we used the average security size instead of the maximum security size. We also assume that a bsl::map
s node size is roughly the size of 4 pointers.
With the use of a bdlma::BufferedSequentialAllocator
, we can be reasonably assured that the memory allocation performance is optimized (i.e., minimal use of dynamic allocation).