Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bdlma_bufferedsequentialallocator
[Package bdlma]

Provide an efficient managed allocator using an external buffer. More...

Namespaces

namespace  bdlma

Detailed Description

Outline
Purpose:
Provide an efficient managed allocator using an external buffer.
Classes:
bdlma::BufferedSequentialAllocator allocator using an external buffer
See also:
Component bdlma_bufferedsequentialpool, Component 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:
   ,----------------------------------.
  ( bdlma::BufferedSequentialAllocator )
   `----------------------------------'
                   |        ctor/dtor
                   |        rewind
                   |
                   V
       ,-----------------------.
      ( bdlma::ManagedAllocator )
       `-----------------------'
                   |        release
                   V
          ,----------------.
         ( bslma::Allocator )
          `----------------'
                            allocate
                            deallocate
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)
  {
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:
      bsls::AlignedBuffer<k_SIZE> bufferStorage;

      bdlma::BufferedSequentialAllocator alloc(bufferStorage.buffer(),
                                               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();
  }
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.
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)
  };

  bsls::AlignedBuffer<k_TOTAL_SIZE> bufferStorage;
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.
  bdlma::BufferedSequentialAllocator bsa(bufferStorage.buffer(),
                                         k_TOTAL_SIZE,
                                         &objectAllocator);
  bsl::map<bsl::string, double> updateMap(&bsa);

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