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

Detailed Description

Outline

Purpose

Provide an efficient managed allocator using a local buffer.

Classes

See also
bdlma_bufferedsequentialallocator

Description

This component provides a concrete mechanism, bdlma::LocalSequentialAllocator, that implements the bdlma::ManagedAllocator protocol to very efficiently allocate heterogeneous memory blocks (of varying, user-specified sizes) from a local buffer. Note that it derives from bdlma::BufferedSequentialAllocator so that the implementations of allocate, deallocate, and release don't need to be instantiated for each user-specified size.

,-------------------------------.
`-------------------------------'
| ctor
V
,----------------------------------.
( bdlma::BufferedSequentialAllocator )
`----------------------------------'
| ctor/dtor
| allocate
| deallocate
| release
| rewind
V
,-----------------------.
`-----------------------'
| release = 0
V
,----------------.
( bslma::Allocator )
`----------------'
allocate = 0
deallocate = 0
Definition bdlma_localsequentialallocator.h:230
Definition bdlma_managedallocator.h:391

If an allocation request exceeds the remaining free memory space in the local buffer, the allocator will fall back to a sequence of dynamically-allocated buffers. The release method releases all memory allocated through the allocator, as does the destructor. Note that, even though a deallocate method is available, it has no effect: individually allocated memory blocks cannot be separately deallocated.

bdlma::LocalSequentialAllocator is typically used when users have a reasonable estimation of the amount of memory needed. This amount of memory would then be created directly on the program stack, and used as the local buffer by this allocator for very fast memory allocation. Whilst the buffer has sufficient capacity, memory allocations will not trigger any dynamic memory allocation, will have optimal locality of reference, and will not require deallocation upon destruction.

Once the local buffer is exhausted, subsequent allocation requests require dynamic memory allocation, and the performance of the allocator degrades.

The main difference between a bdlma::LocalSequentialAllocator and a bdlma::BufferedSequentialAllocator is that this class internally maintains a buffer, rather than being given one at construction time.

Usage

This section illustrates intended use of this component.

Example 1: Recommended Usage

Suppose we have a function which takes a map of items to update in some database:

typedef bsl::string DatabaseKey;
typedef bsl::string DatabaseValue;
void updateRecords_1(const bsl::map<DatabaseKey, DatabaseValue>& values)
{
it = values.begin(), end = values.end();
it != end;
++it) {
bsl::stringbuf stringBuf;
bsl::ostream ostr(&stringBuf);
ostr << "UPDATE myTable SET myValue = '" << it->first << "' WHERE "
"myKey = '" << it->second << "'";
// execute query using 'stringBuf.str()'
}
}
Definition bslstl_string.h:1281
Definition bslstl_stringbuf.h:245
Definition bslstl_map.h:619
BloombergLP::bslstl::TreeIterator< const value_type, Node, difference_type > const_iterator
Definition bslstl_map.h:724
iterator end() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_map.h:2759
iterator begin() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_map.h:2751

We call this method a lot, and after profiling, we notice that it's contributing a significant proportion of time, due to the allocations it is making. We decide to see whether a LocalSequentialAllocator would help.

First, use a bslma::TestAllocator to track the typical memory usage:

void updateRecords_2(const bsl::map<DatabaseKey, DatabaseValue>& values)
{
it = values.begin(), end = values.end();
it != end;
++it) {
bsl::stringbuf stringBuf(&ta);
bsl::ostream ostr(&stringBuf);
ostr << "UPDATE myTable SET myValue = '" << it->first << "' WHERE "
"myKey = '" << it->second << "'";
// execute query using 'stringBuf.str()'
bsl::cout << "In use: " << ta.numBytesInUse() << '\n';
}
bsl::cout << "Max: " << ta.numBytesMax() << '\n';
}
Definition bslma_testallocator.h:384
bsls::Types::Int64 numBytesInUse() const
Definition bslma_testallocator.h:1111
bsls::Types::Int64 numBytesMax() const
Definition bslma_testallocator.h:1117

Then we run our program again, and observe the following output:

In use: 77
In use: 77
In use: 77
In use: 77
In use: 77
Max: 129

It looks like 129 is a good choice for the size of our allocator, so we go with that:

void updateRecords_3(const bsl::map<DatabaseKey, DatabaseValue>& values)
{
it = values.begin(), end = values.end();
it != end;
++it) {
lsa.release();
bsl::stringbuf stringBuf(&lsa);
bsl::ostream ostr(&stringBuf);
ostr << "UPDATE myTable SET myValue = '" << it->first << "' WHERE "
"myKey = '" << it->second << "'";
// execute query using 'stringBuf.str()'
}
}
void release() BSLS_KEYWORD_OVERRIDE
Definition bdlma_bufferedsequentialallocator.h:518

Note that we release at the end of every iteration, as the deallocate method is a no-op, so without this, subsequent memory would be allocated from the default allocator (or the allocator passed to bsa at construction).

Finally, we re-profile our code to determine whether the addition of a LocalSequentialAllocator helped.