Quick Links: |
Provide an allocator to manage pools of varying object sizes. More...
Namespaces | |
namespace | bdlma |
bdlma::ConcurrentMultipoolAllocator | allocator managing varying size pools |
bdlma::ConcurrentMultipoolAllocator
, that implements the bdlma::ManagedAllocator
protocol and provides an allocator that maintains a configurable number of bdlma::ConcurrentPool
objects, each dispensing memory blocks of a unique size. The bdlma::ConcurrentPool
objects are placed in an array, starting at index 0, with each successive pool managing memory blocks of a size twice that of the previous pool. Each multipool allocation (deallocation) request allocates memory from (returns memory to) the internal pool managing memory blocks of the smallest size not less than the requested size, or else from a separately managed list of memory blocks, if no internal pool managing memory block of sufficient size exists. Both the release
method and the destructor of a bdlma::ConcurrentMultipoolAllocator
release all memory currently allocated via the object. ,-----------------------------------. ( bdlma::ConcurrentMultipoolAllocator ) `-----------------------------------' | ctor/dtor | maxPooledBlockSize | numPools | reserveCapacity V ,-----------------------. ( bdlma::ManagedAllocator ) `-----------------------' | release V ,-----------------. ( bslma::Allocator ) `-----------------' allocate deallocate
bdlma::ConcurrentMultipoolAllocator
and a bdlma::ConcurrentMultipool
is that, very often, a bdlma::ConcurrentMultipoolAllocator
is managed through a bslma::Allocator
pointer. Hence, every call to the allocate
method invokes a virtual function call, which is slower than invoking the non-virtual allocate
method on a bdlma::ConcurrentMultipool
. However, since bslma::Allocator *
is widely used across BDE interfaces, bdlma::ConcurrentMultipoolAllocator
is more general purposed than a bdlma::ConcurrentMultipool
. bdlma::ConcurrentMultipoolAllocator
, clients can optionally configure: GROWTH STRATEGY -- geometrically growing chunk size starting from 1 (in terms of the number of memory blocks per chunk), or fixed chunk size, specified as either:
MAX BLOCKS PER CHUNK -- the maximum number of memory blocks within a chunk, specified as either:
BASIC ALLOCATOR -- the allocator used to supply memory (to replenish an internal pool, or directly if the maximum block size is exceeded). If not specified, the currently installed default allocator (see bslma_default
) is used.
N
with respective block sizes ranging from 2^3 = 8
to 2^(N+2)
. By default, the initial chunk size, (i.e., the number of blocks of a given size allocated at once to replenish a pool's memory) is 1, and each pool's chunk size grows geometrically until it reaches an implementation-defined maximum, at which it is capped. Finally, unless otherwise specified, all memory comes from the allocator that was the currently installed default allocator at the time the bdlma::ConcurrentMultipoolAllocator
was created. bdlma::ConcurrentMultipoolAllocator
can be used to supply memory to node-based data structures such as bsl::set
, bsl::list
or bsl::map
. Suppose we are implementing a container of named graphs data structure, where a graph is defined by a set of edges and nodes. The various fixed-sized nodes can be efficiently allocated by a bdlma::ConcurrentMultipoolAllocator
. my_Edge
, is defined as follows: class my_Node; class my_Edge { // This class represents an edge within a graph. Both ends of the // edge must be connected to nodes. // DATA my_Node *d_first; // first node my_Node *d_second; // second node // ... public: // CREATORS my_Edge(my_Node *first, my_Node *second); // Create an edge that connects to the specified 'first' and // 'second' nodes. // ... }; // CREATORS my_Edge::my_Edge(my_Node *first, my_Node *second) : d_first(first), d_second(second) { }
my_Node
, is defined as follows: class my_Node { // This class represents a node within a graph. A node can be // connected to any number of edges. // DATA bsl::set<my_Edge *> d_edges; // set of edges this node connects to // ... private: // Not implemented: my_Node(const my_Node&); public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(my_Node, bslma::UsesBslmaAllocator); // CREATORS explicit my_Node(bslma::Allocator *basicAllocator = 0); // Create a node not connected to any other nodes. Optionally // specify a 'basicAllocator' used to supply memory. If // 'basicAllocator' is 0, the currently installed default allocator // is used. // ... }; // CREATORS my_Node::my_Node(bslma::Allocator *basicAllocator) : d_edges(basicAllocator) { }
my_Graph
, as follows: class my_Graph { // This class represents a graph having sets of nodes and edges. // DATA bsl::set<my_Edge> d_edges; // set of edges in this graph bsl::set<my_Node> d_nodes; // set of nodes in this graph // ... private: // Not implemented: my_Graph(const my_Graph&); public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(my_Graph, bslma::UsesBslmaAllocator); // CREATORS explicit my_Graph(bslma::Allocator *basicAllocator = 0); // Create an empty graph. Optionally specify a 'basicAllocator' // used to supply memory. If 'basicAllocator' is 0, the currently // installed default allocator is used. // ... }; my_Graph::my_Graph(bslma::Allocator *basicAllocator) : d_edges(basicAllocator) , d_nodes(basicAllocator) { }
my_NamedGraphContainer
, is defined as follows: class my_NamedGraphContainer { // This class stores a map that index graph names to graph objects. // DATA bsl::map<bsl::string, my_Graph> d_graphMap; // map from graph names to // graph private: // Not implemented: my_NamedGraphContainer(const my_NamedGraphContainer&); public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(my_NamedGraphContainer, bslma::UsesBslmaAllocator); // CREATORS explicit my_NamedGraphContainer(bslma::Allocator *basicAllocator = 0); // Create an empty named graph container. Optionally specify a // 'basicAllocator' used to supply memory. If 'basicAllocator' is // 0, the currently installed default allocator is used. // ... }; // CREATORS my_NamedGraphContainer::my_NamedGraphContainer( bslma::Allocator *basicAllocator) : d_graphMap(basicAllocator) { }
main
, we can create a bdlma::ConcurrentMultipoolAllocator
and pass it to our my_NamedGraphContainer
. Since we know that the maximum block size needed is 32 (comes from sizeof(my_Graph)
), we can calculate the number of pools needed by using the formula specified in the "configuration at construction" section: largestPoolSize < 2 ^ (N + 2).
N
that satisfies this relationship is 3: enum { k_NUM_POOLS = 3 }; bdlma::ConcurrentMultipoolAllocator basicAllocator(k_NUM_POOLS); my_NamedGraphContainer container(&basicAllocator);