BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdlma_localsequentialallocator

Detailed Description

Provide an efficient managed allocator using a local buffer.

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:392

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:1252
Definition bslstl_map.h:653
BloombergLP::bslstl::TreeIterator< const value_type, Node, difference_type > const_iterator
Definition bslstl_map.h:758
iterator end() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_map.h:3308
iterator begin() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_map.h:3300
basic_stringbuf< char, char_traits< char >, allocator< char > > stringbuf
Definition bslstl_iosfwd.h:93

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:402
bsls::Types::Int64 numBytesInUse() const
Definition bslma_testallocator.h:1394
bsls::Types::Int64 numBytesMax() const
Definition bslma_testallocator.h:1400

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:524

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.