BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bdlma_bufferedsequentialallocator

Detailed Description

Outline

Warning

Purpose

Provide an efficient managed allocator using an external buffer.

Classes

See also
bdlma_bufferedsequentialpool, bdlma_sequentialallocator

Description

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:

,----------------------------------.
`----------------------------------'
| ctor/dtor
| rewind
|
V
,-----------------------.
( bdlma::ManagedAllocator )
`-----------------------'
| release
V
,----------------.
`----------------'
allocate
deallocate
Definition bdlma_bufferedsequentialallocator.h:265
Definition bslma_allocator.h:457

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.

Optional maxBufferSize Parameter

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.

Warning

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).

Usage

This section illustrates intended use of this component.

Example 1: Using bdlma::BufferedSequentialAllocator with Exact Calculation

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:

double calculate(const bsl::vector<double>& data)
{
Definition bslstl_vector.h:1025

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:

enum { k_SIZE = 3 * 100 * sizeof(double) };

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:

k_SIZE);
bsl::vector<double> v1(&alloc); v1.reserve(100);
bsl::vector<double> v2(&alloc); v2.reserve(100);
bsl::vector<double> v3(&alloc); v3.reserve(100);
return data.empty() ? 0.0 : data.front();
}
Definition bsls_alignedbuffer.h:261
char * buffer()
Definition bsls_alignedbuffer.h:294

By making use of a bdlma::BufferedSequentialAllocator, all dynamic memory allocation is eliminated in the above example.

Example 2: Using bdlma::BufferedSequentialAllocator with Fallback

Suppose we are receiving updates for price quotes for a list of securities through the following function:

void receivePriceQuotes(bsl::map<bsl::string, double> *updateMap);
// Load into the specified 'updateMap' updates for price quotes for a
// list of securities.
Definition bslstl_map.h:619

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:

enum {
k_NUM_SECURITIES = 100,
k_TREE_NODE_SIZE = sizeof(bsl::map<bsl::string, double>::value_type)
+ sizeof(void *) * 4,
k_AVERAGE_SECURITY_LENGTH = 5,
k_TOTAL_SIZE = k_NUM_SECURITIES *
(k_TREE_NODE_SIZE + k_AVERAGE_SECURITY_LENGTH)
};
Definition bslstl_pair.h:1210

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::maps node size is roughly the size of 4 pointers.

k_TOTAL_SIZE,
&objectAllocator);
receivePriceQuotes(&updateMap);

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).