BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bdlma_multipoolallocator.h
Go to the documentation of this file.
1/// @file bdlma_multipoolallocator.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlma_multipoolallocator.h -*-C++-*-
8#ifndef INCLUDED_BDLMA_MULTIPOOLALLOCATOR
9#define INCLUDED_BDLMA_MULTIPOOLALLOCATOR
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bdlma_multipoolallocator bdlma_multipoolallocator
15/// @brief Provide a memory-pooling allocator of heterogeneous block sizes.
16/// @addtogroup bdl
17/// @{
18/// @addtogroup bdlma
19/// @{
20/// @addtogroup bdlma_multipoolallocator
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bdlma_multipoolallocator-purpose"> Purpose</a>
25/// * <a href="#bdlma_multipoolallocator-classes"> Classes </a>
26/// * <a href="#bdlma_multipoolallocator-description"> Description </a>
27/// * <a href="#bdlma_multipoolallocator-configuration-at-construction"> Configuration at Construction </a>
28/// * <a href="#bdlma_multipoolallocator-usage"> Usage </a>
29/// * <a href="#bdlma_multipoolallocator-example-1-using-a-bdlma-multipoolallocator"> Example 1: Using a bdlma::MultipoolAllocator </a>
30/// * <a href="#bdlma_multipoolallocator-example-2-performance-of-a-bdlma-multipoolallocator"> Example 2: Performance of a bdlma::MultipoolAllocator </a>
31///
32/// # Purpose {#bdlma_multipoolallocator-purpose}
33/// Provide a memory-pooling allocator of heterogeneous block sizes.
34///
35/// # Classes {#bdlma_multipoolallocator-classes}
36///
37/// - bdlma::MultipoolAllocator: allocator managing varying-size memory pools
38///
39/// @see bdlma_pool, bdlma_multipool
40///
41/// # Description {#bdlma_multipoolallocator-description}
42/// This component provides a general-purpose, managed allocator,
43/// `bdlma::MultipoolAllocator`, that implements the `bdlma::ManagedAllocator`
44/// protocol and provides an allocator that maintains a configurable number of
45/// `bdlma::Pool` objects, each dispensing maximally-aligned memory blocks of a
46/// unique size. The `bdlma::Pool` objects are placed in an array, starting at
47/// index 0, with each successive pool managing memory blocks of a size twice
48/// that of the previous pool. Each multipool allocation (deallocation) request
49/// allocates memory from (returns memory to) the internal pool managing memory
50/// blocks of the smallest size not less than the requested size, or else from a
51/// separately managed list of memory blocks, if no internal pool managing
52/// memory blocks of sufficient size exists. Both the `release` method and the
53/// destructor of a `bdlma::MultipoolAllocator` release all memory currently
54/// allocated via the object.
55/// @code
56/// ,-------------------------.
57/// ( bdlma::MultipoolAllocator )
58/// `-------------------------'
59/// | ctor/dtor
60/// | maxPooledBlockSize
61/// | numPools
62/// | reserveCapacity
63/// V
64/// ,-----------------------.
65/// ( bdlma::ManagedAllocator )
66/// `-----------------------'
67/// | release
68/// V
69/// ,----------------.
70/// ( bslma::Allocator )
71/// `----------------'
72/// allocate
73/// deallocate
74/// @endcode
75/// The main difference between a `bdlma::MultipoolAllocator` and a
76/// `bdlma::Multipool` is that, very often, a `bdlma::MultipoolAllocator` is
77/// managed through a `bslma::Allocator` pointer. Hence, every call to the
78/// `allocate` method invokes a virtual function call, which is slower than
79/// invoking the non-virtual `allocate` method on a `bdlma::Multipool`.
80/// However, since `bslma::Allocator *` is widely used across BDE interfaces,
81/// `bdlma::MultipoolAllocator` is more general purpose than `bdlma::Multipool`.
82///
83/// ## Configuration at Construction {#bdlma_multipoolallocator-configuration-at-construction}
84///
85///
86/// When creating a `bdlma::MultipoolAllocator`, clients can optionally
87/// configure:
88///
89/// 1. NUMBER OF POOLS -- the number of internal pools (the block size managed
90/// by the first pool is eight bytes, with each successive pool managing
91/// blocks of a size twice that of the previous pool).
92/// 2. GROWTH STRATEGY -- geometrically growing chunk size starting from 1 (in
93/// terms of the number of memory blocks per chunk), or fixed chunk size,
94/// specified as either:
95/// - the unique growth strategy for all pools, or
96/// - (if the number of pools is specified) an array of growth strategies
97/// corresponding to each individual pool.
98/// If the growth strategy is not specified, geometric growth is used for all
99/// pools.
100/// 3. MAX BLOCKS PER CHUNK -- the maximum number of memory blocks within a
101/// chunk, specified as either:
102/// - the unique maximum-blocks-per-chunk value for all of the pools, or
103/// - an array of maximum-blocks-per-chunk values corresponding to each
104/// individual pool.
105/// If the maximum blocks per chunk is not specified, an
106/// implementation-defined default value is used. Note that the maximum
107/// blocks per chunk can be configured only if the number of pools is also
108/// configured.
109/// 4. BASIC ALLOCATOR -- the allocator used to supply memory (to replenish an
110/// internal pool, or directly if the maximum block size is exceeded). If
111/// not specified, the currently installed default allocator is used (see
112/// @ref bslma_default ).
113///
114/// A default-constructed multipool allocator has a relatively small,
115/// implementation-defined number of pools, `N`, with respective block sizes
116/// ranging from `2^3 = 8` to `2^(N+2)`. By default, the initial chunk size,
117/// (i.e., the number of blocks of a given size allocated at once to replenish a
118/// pool's memory) is 1, and each pool's chunk size grows geometrically until it
119/// reaches an implementation-defined maximum, at which it is capped. Finally,
120/// unless otherwise specified, all memory comes from the allocator that was the
121/// currently installed default allocator at the time the
122/// `bdlma::MultipoolAllocator` was created.
123///
124/// Using the various pooling options described above, we can configure the
125/// number of pools maintained, whether replenishment should be adaptive (i.e.,
126/// geometric starting with 1) or fixed at a maximum chunk size, what that
127/// maximum chunk size should be (which need not be an integral power of 2), and
128/// the underlying allocator used to supply memory. Note that both GROWTH
129/// STRATEGY and MAX BLOCKS PER CHUNK can be specified separately either as a
130/// single value applying to all of the maintained pools, or as an array of
131/// values, with the elements applying to each individually maintained pool.
132///
133/// ## Usage {#bdlma_multipoolallocator-usage}
134///
135///
136/// This section illustrates intended use of this component.
137///
138/// ### Example 1: Using a bdlma::MultipoolAllocator {#bdlma_multipoolallocator-example-1-using-a-bdlma-multipoolallocator}
139///
140///
141/// A `bdlma::MultipoolAllocator` can be used to supply memory to node-based
142/// data structures such as `bsl::set`, `bsl::list`, and `bsl::map`. Suppose we
143/// are implementing a container of named graphs, where a graph is defined by a
144/// set of edges and a set of nodes. The various fixed-sized nodes and edges
145/// can be efficiently allocated by a `bdlma::MultipoolAllocator`.
146///
147/// First, the edge class, `my_Edge`, is defined as follows:
148/// @code
149/// class my_Node;
150///
151/// class my_Edge {
152/// // This class represents an edge within a graph. Both ends of an edge
153/// // must be connected to nodes.
154///
155/// // DATA
156/// my_Node *d_first; // first node
157/// my_Node *d_second; // second node
158///
159/// // ...
160///
161/// public:
162/// // CREATORS
163/// my_Edge(my_Node *first, my_Node *second);
164/// // Create an edge that connects to the specified 'first' and
165/// // 'second' nodes.
166///
167/// // ...
168/// };
169///
170/// // CREATORS
171/// my_Edge::my_Edge(my_Node *first, my_Node *second)
172/// : d_first(first)
173/// , d_second(second)
174/// {
175/// }
176/// @endcode
177/// Then, the node class, `my_Node`, is defined as follows:
178/// @code
179/// class my_Node {
180/// // This class represents a node within a graph. A node can be
181/// // connected to any number of edges.
182///
183/// // DATA
184/// bsl::set<my_Edge *> d_edges; // set of edges this node connects to
185///
186/// // ...
187///
188/// private:
189/// // Not implemented:
190/// my_Node(const my_Node&);
191///
192/// public:
193/// // TRAITS
194/// BSLMF_NESTED_TRAIT_DECLARATION(my_Node, bslma::UsesBslmaAllocator);
195///
196/// // CREATORS
197/// explicit my_Node(bslma::Allocator *basicAllocator = 0);
198/// // Create a node not connected to any other nodes. Optionally
199/// // specify a 'basicAllocator' used to supply memory. If
200/// // 'basicAllocator' is 0, the currently installed default allocator
201/// // is used.
202///
203/// // ...
204/// };
205///
206/// // CREATORS
207/// my_Node::my_Node(bslma::Allocator *basicAllocator)
208/// : d_edges(basicAllocator)
209/// {
210/// }
211/// @endcode
212/// Then, we define the graph class, `my_Graph`, as follows:
213/// @code
214/// class my_Graph {
215/// // This class represents a graph having sets of nodes and edges.
216///
217/// // DATA
218/// bsl::set<my_Edge> d_edges; // set of edges in this graph
219/// bsl::set<my_Node> d_nodes; // set of nodes in this graph
220///
221/// // ...
222///
223/// private:
224/// // Not implemented:
225/// my_Graph(const my_Graph&);
226///
227/// public:
228/// // TRAITS
229/// BSLMF_NESTED_TRAIT_DECLARATION(my_Graph, bslma::UsesBslmaAllocator);
230///
231/// // CREATORS
232/// explicit my_Graph(bslma::Allocator *basicAllocator = 0);
233/// // Create an empty graph. Optionally specify a 'basicAllocator'
234/// // used to supply memory. If 'basicAllocator' is 0, the currently
235/// // installed default allocator is used.
236///
237/// // ...
238/// };
239///
240/// my_Graph::my_Graph(bslma::Allocator *basicAllocator)
241/// : d_edges(basicAllocator)
242/// , d_nodes(basicAllocator)
243/// {
244/// }
245/// @endcode
246/// Next, the container for the collection of named graphs,
247/// `my_NamedGraphContainer`, is defined as follows:
248/// @code
249/// class my_NamedGraphContainer {
250/// // This class stores a map that indexes graph names to graph objects.
251///
252/// // DATA
253/// bsl::map<bsl::string, my_Graph> d_graphMap; // map from graph name to
254/// // graph
255///
256/// private:
257/// // Not implemented:
258/// my_NamedGraphContainer(const my_NamedGraphContainer&);
259///
260/// public:
261/// // TRAITS
262/// BSLMF_NESTED_TRAIT_DECLARATION(my_NamedGraphContainer,
263/// bslma::UsesBslmaAllocator);
264///
265/// // CREATORS
266/// explicit my_NamedGraphContainer(bslma::Allocator *basicAllocator = 0);
267/// // Create an empty named graph container. Optionally specify a
268/// // 'basicAllocator' used to supply memory. If 'basicAllocator' is
269/// // 0, the currently installed default allocator is used.
270///
271/// // ...
272/// };
273///
274/// // CREATORS
275/// my_NamedGraphContainer::my_NamedGraphContainer(
276/// bslma::Allocator *basicAllocator)
277/// : d_graphMap(basicAllocator)
278/// {
279/// }
280/// @endcode
281/// Finally, in `main`, we can create a `bdlma::MultipoolAllocator` and pass it
282/// to our `my_NamedGraphContainer`. Since we know that the maximum block size
283/// needed is 32 (`sizeof(my_Graph)`), we can calculate the number of pools
284/// needed by using the formula given in the "Configuration at Construction"
285/// section:
286/// @code
287/// largestPoolSize == 2 ^ (N + 2)
288/// @endcode
289/// When solved for the above equation, the smallest `N` that satisfies this
290/// relationship is 3:
291/// @code
292/// int main()
293/// {
294/// enum { k_NUM_POOLS = 3 };
295///
296/// bdlma::MultipoolAllocator multipoolAllocator(NUM_POOLS);
297///
298/// my_NamedGraphContainer container(&multipoolAllocator);
299/// }
300/// @endcode
301///
302/// ### Example 2: Performance of a bdlma::MultipoolAllocator {#bdlma_multipoolallocator-example-2-performance-of-a-bdlma-multipoolallocator}
303///
304///
305/// A `bdlma::MultipoolAllocator` can greatly improve efficiency when it is used
306/// to supply memory to node-based data structures that frequently both insert
307/// and remove nodes, while growing to significant size before being destroyed.
308/// The following experiment will illustrate the benefits of using a
309/// `bdlma::MultipoolAllocator` under this scenario by comparing the following 3
310/// different allocator uses:
311///
312/// 1. Using the `bslma::NewDeleteAllocator`.
313/// 2. Using a `bdlma::MultipoolAllocator` as a substitute for the
314/// `bslma::NewDeleteAllocator`.
315/// 3. Exploiting the managed aspect of `bdlma::MultipoolAllocator` by avoiding
316/// invocation of the destructor of the data structure, since the
317/// destruction of the allocator will automatically reclaim all memory.
318///
319/// First, we create a test data structure that contains three `bsl::list`s.
320/// Each list holds a different type of object, where each type has a different
321/// size. For simplicity, we create these different object types as different
322/// instantiations of a template class, parameterized on the object size:
323/// @code
324/// template <int OBJECT_SIZE>
325/// class my_TestObject {
326///
327/// // DATA
328/// char d_data[OBJECT_SIZE];
329/// };
330/// @endcode
331/// Again, for simplicity, the sizes of these objects are chosen to be 20, 40,
332/// and 80, instead of being parameterized as part of the test data structure:
333/// @code
334/// class my_TestDataStructure {
335///
336/// // PRIVATE TYPES
337/// typedef my_TestObject<20> Obj1;
338/// typedef my_TestObject<40> Obj2;
339/// typedef my_TestObject<80> Obj3;
340///
341/// // DATA
342/// bsl::list<Obj1> d_list1;
343/// bsl::list<Obj2> d_list2;
344/// bsl::list<Obj3> d_list3;
345/// @endcode
346/// The test will consist of the following steps:
347///
348/// 1. Push back into `d_list1`, then `d_list2`, then `d_list3`.
349/// 2. Repeat #1.
350/// 3. Pop front from `d_list1`, then `d_list2`, then `d_list3`.
351///
352/// The above 3 steps will be repeated `n` times, where `n` is a parameter
353/// specified by the user. This process will both grow the list and incorporate
354/// a large number of node removals. Note that nodes are removed from the front
355/// of the list to simulate a particular real-world usage, where nodes removed
356/// are rarely those recently added (this also removes the possibility of noise
357/// from potential optimizations with relinquishing memory blocks that are most
358/// recently allocated).
359/// @code
360/// public:
361/// // CREATORS
362/// my_TestDataStructure(bslma::Allocator *basicAllocator = 0);
363///
364/// // MANIPULATORS
365/// void pop();
366///
367/// void push();
368/// };
369///
370/// // CREATORS
371/// my_TestDataStructure::my_TestDataStructure(
372/// bslma::Allocator *basicAllocator)
373/// : d_list1(basicAllocator)
374/// , d_list2(basicAllocator)
375/// , d_list3(basicAllocator)
376/// {
377/// }
378/// @endcode
379/// The `push` method will push into the 3 `bsl::list` objects managed by
380/// `my_TestDataStructure` sequentially. Similarly, the `pop` method will pop
381/// from the lists sequentially:
382/// @code
383/// // MANIPULATORS
384/// void my_TestDataStructure::push()
385/// {
386/// // Push to the back of the 3 lists.
387///
388/// d_list1.push_back(Obj1());
389/// d_list2.push_back(Obj2());
390/// d_list3.push_back(Obj3());
391/// }
392///
393/// void my_TestDataStructure::pop()
394/// {
395/// // Pop from the front of the 3 lists.
396///
397/// d_list1.pop_front();
398/// d_list2.pop_front();
399/// d_list3.pop_front();
400/// }
401/// @endcode
402/// The above `push` and `pop` methods will allow us to evaluate the cost to add
403/// and remove nodes using different allocators. To evaluate the cost of
404/// destruction (and hence deallocation of all allocated memory in the list
405/// objects), we supply a `static` `test` method within a `my_TestUtil` class to
406/// create the test mechanism, run the test, and destroy the test mechanism.
407///
408/// The `test` method accepts a `testLengthFactor` argument specified by the
409/// user to control the length of the test. The effect of `testLengthFactor` is
410/// shown below:
411/// @code
412/// testLengthFactor test size n iterations
413/// ---------------- ---------------- -------- ----------
414/// 4 10^4 = 10000 1 10000
415/// 10 1000
416/// 100 100
417/// 1000 10
418/// 10000 1
419///
420/// 5 10^5 = 100000 1 100000
421/// 10 10000
422/// 100 1000
423/// 1000 100
424/// 10000 10
425/// 100000 1
426///
427/// 6 10^6 = 1000000 1 1000000
428/// 10 100000
429/// 100 10000
430/// 1000 1000
431/// 10000 100
432/// 100000 10
433/// 1000000 1
434///
435/// // ...
436/// @endcode
437/// For each row of the specified `testLengthFactor`, a `my_TestDataStructure`
438/// will be created "iterations" times, and each time the lists within the data
439/// structure will grow by invoking `push` twice and `pop` once. Note that "n"
440/// measures the effect of insertion and removal of nodes, while "iterations"
441/// measures the effect of construction and destruction of entire lists of
442/// nodes.
443///
444/// The `test` method also accepts a `bslma::Allocator *` to be used as the
445/// allocator used to construct the test mechanism and its internal lists:
446/// @code
447/// class my_TestUtil {
448///
449/// public:
450/// // CLASS METHODS
451/// static
452/// void test(int testLengthFactor, bslma::Allocator *basicAllocator)
453/// {
454/// int n = 1;
455/// int iterations = 1;
456///
457/// for (int i = 0; i < testLengthFactor; ++i) {
458/// iterations *= 10;
459/// }
460///
461/// for (int i = 0; i <= testLengthFactor; ++i) {
462/// bsls::Stopwatch timer;
463/// timer.start();
464///
465/// for (int j = 0; j < iterations; ++j) {
466/// my_TestDataStructure tds(basicAllocator);
467///
468/// // Testing cost of insertion and deletion.
469///
470/// for (int k = 0; k < n; ++k) {
471/// tds.push();
472/// tds.push();
473/// tds.pop();
474/// }
475///
476/// // Testing cost of destruction.
477/// }
478///
479/// timer.stop();
480///
481/// printf("%d\t%d\t%d\t%1.6f\n", testLengthFactor,
482/// n,
483/// iterations,
484/// timer.elapsedTime());
485///
486/// n *= 10;
487/// iterations /= 10;
488/// }
489/// }
490/// @endcode
491/// Next, to fully test the benefit of being able to relinquish all allocated
492/// memory at once, we use the `testManaged` method, which accepts only managed
493/// allocators. Instead of creating the test mechanism on the stack, the test
494/// mechanism will be created on the heap. After running the test, the
495/// `release` method of the allocator will reclaim all outstanding allocations
496/// at once, eliminating the need to run the destructor of the test mechanism:
497/// @code
498/// static
499/// void testManaged(int testLengthFactor,
500/// bdlma::ManagedAllocator *managedAllocator)
501/// {
502/// int n = 1;
503/// int iterations = 1;
504///
505/// for (int i = 0; i < testLengthFactor; ++i) {
506/// iterations *= 10;
507/// }
508///
509/// for (int i = 0; i <= testLengthFactor; ++i) {
510/// bsls::Stopwatch timer;
511/// timer.start();
512///
513/// for (int j = 0; j < iterations; ++j) {
514/// my_TestDataStructure *tmPtr = new(*managedAllocator)
515/// my_TestDataStructure(managedAllocator);
516///
517/// // Testing cost of insertion and deletion.
518///
519/// for (int k = 0; k < n; ++k) {
520/// tmPtr->push();
521/// tmPtr->push();
522/// tmPtr->pop();
523/// }
524///
525/// // Testing cost of destruction.
526///
527/// managedAllocator->release();
528/// }
529///
530/// timer.stop();
531///
532/// printf("%d\t%d\t%d\t%1.6f\n", testLengthFactor,
533/// n,
534/// iterations,
535/// timer.elapsedTime());
536///
537/// n *= 10;
538/// iterations /= 10;
539/// }
540/// }
541/// };
542/// @endcode
543/// Finally, in main, we run the test with the different allocators and
544/// different allocator configurations based on command line arguments:
545/// @code
546/// {
547/// int testLengthFactor = 5;
548/// const int NUM_POOLS = 10;
549///
550/// if (argc > 2) {
551/// testLengthFactor = bsl::atoi(argv[2]);
552/// }
553///
554/// char growth = 'g';
555/// if (argc > 3) {
556/// growth = argv[3][0];
557/// if (growth != 'g' && growth != 'c') {
558/// printf("[g]eometric or [c]onstant growth must be used\n");
559/// return -1;
560/// }
561/// }
562///
563/// int maxChunkSize = 32;
564/// if (argc > 4) {
565/// maxChunkSize = bsl::atoi(argv[4]);
566/// if (maxChunkSize <= 0) {
567/// printf("maxChunkSize must be >= 1");
568/// }
569/// }
570///
571/// bsls::BlockGrowth::Strategy strategy = growth == 'g'
572/// ? bsls::BlockGrowth::BSLS_GEOMETRIC
573/// : bsls::BlockGrowth::BSLS_CONSTANT;
574///
575/// printf("\nNew Delete Allocator:\n\n");
576/// {
577/// bslma::Allocator *nda = bslma::NewDeleteAllocator::allocator(0);
578/// my_TestUtil::test(testLengthFactor, nda);
579/// }
580///
581/// printf("\nMultipool Allocator with [%c], [%d]:\n\n", growth,
582/// maxChunkSize);
583/// {
584/// bdlma::MultipoolAllocator ma(NUM_POOLS, strategy, maxChunkSize);
585/// my_TestUtil::test(testLengthFactor, &ma);
586/// }
587///
588/// printf("\nMultipool Allocator Managed with [%c], [%d]:\n\n", growth,
589/// maxChunkSize);
590/// {
591/// bdlma::MultipoolAllocator ma(NUM_POOLS, strategy, maxChunkSize);
592/// my_TestUtil::testManaged(testLengthFactor, &ma);
593/// }
594///
595/// return 0;
596/// }
597/// @endcode
598/// An excerpt of the results of the test running on IBM under optimized mode,
599/// using default constructed `bdlma::MultipoolAllocator` parameters, is shown
600/// below:
601/// @code
602/// New Delete Allocator:
603///
604/// 6 1 1000000 3.006253
605/// 6 10 100000 2.369734
606/// 6 100 10000 2.598567
607/// 6 1000 1000 2.604546
608/// 6 10000 100 2.760319
609/// 6 100000 10 3.085765
610/// 6 1000000 1 4.465030
611///
612/// Multipool Allocator with [g], [32]:
613///
614/// 6 1 1000000 0.766064
615/// 6 10 100000 0.408509
616/// 6 100 10000 0.357019
617/// 6 1000 1000 0.436448
618/// 6 10000 100 0.643206
619/// 6 100000 10 0.932662
620/// 6 1000000 1 0.938906
621///
622/// Multipool Allocator Managed with [g], [32]:
623///
624/// 6 1 1000000 1.958663
625/// 6 10 100000 0.463185
626/// 6 100 10000 0.371201
627/// 6 1000 1000 0.357816
628/// 6 10000 100 0.368082
629/// 6 100000 10 0.388422
630/// 6 1000000 1 0.529167
631/// @endcode
632/// It is clear that using a `bdlma::MultipoolAllocator` results in an
633/// improvement in memory allocation by a factor of about 4. Furthermore, if
634/// the managed aspect of the multipool allocator is exploited, the cost of
635/// destruction rapidly decreases in relative terms as the list grows larger
636/// (increasing `n`).
637/// @}
638/** @} */
639/** @} */
640
641/** @addtogroup bdl
642 * @{
643 */
644/** @addtogroup bdlma
645 * @{
646 */
647/** @addtogroup bdlma_multipoolallocator
648 * @{
649 */
650
651#include <bdlscm_version.h>
652
654#include <bdlma_multipool.h>
655
656#include <bslma_allocator.h>
657
658#include <bsls_keyword.h>
659#include <bsls_performancehint.h>
660#include <bsls_types.h>
661
662
663namespace bdlma {
664
665 // ========================
666 // class MultipoolAllocator
667 // ========================
668
669/// This class implements the `bdlma::ManagedAllocator` protocol to provide
670/// an allocator that maintains a configurable number of `bdlma::Pool`
671/// objects, each dispensing memory blocks of a unique size. The
672/// `bdlma::Pool` objects are placed in an array, with each successive pool
673/// managing memory blocks of size twice that of the previous pool. Each
674/// multipool allocation (deallocation) request allocates memory from
675/// (returns memory to) the internal pool having the smallest block size not
676/// less than the requested size, or, if no pool manages memory blocks of
677/// sufficient sized, from a separately managed list of memory blocks. Both
678/// the `release` method and the destructor of a `bdlma::MultipoolAllocator`
679/// release all memory currently allocated via the object.
680///
681/// See @ref bdlma_multipoolallocator
683
684 // DATA
685 Multipool d_multipool; // manager for allocated memory blocks
686
687 private:
688 // NOT IMPLEMENTED
690 MultipoolAllocator& operator=(const MultipoolAllocator&);
691
692 public:
693 // CREATORS
694
695 explicit
696 MultipoolAllocator(bslma::Allocator *basicAllocator = 0);
697 explicit
698 MultipoolAllocator(int numPools, bslma::Allocator *basicAllocator = 0);
699 explicit
701 bslma::Allocator *basicAllocator = 0);
703 bsls::BlockGrowth::Strategy growthStrategy,
704 bslma::Allocator *basicAllocator = 0);
705 /// Create a multipool allocator. Optionally specify `numPools`,
706 /// indicating the number of internally created `bdlma::Pool` objects;
707 /// the block size of the first pool is 8 bytes, with the block size of
708 /// each additional pool successively doubling. If `numPools` is not
709 /// specified, an implementation-defined number of pools `N` -- covering
710 /// memory blocks ranging in size from `2^3 = 8` to `2^(N+2)` -- are
711 /// created. Optionally specify a `growthStrategy` indicating whether
712 /// the number of blocks allocated at once for every internally created
713 /// `bdlma::Pool` should be either fixed or grow geometrically, starting
714 /// with 1. If `growthStrategy` is not specified, the allocation
715 /// strategy for each internally created `bdlma::Pool` object is
716 /// geometric, starting from 1. If `numPools` and `growthStrategy` are
717 /// specified, optionally specify a `maxBlocksPerChunk`, indicating the
718 /// maximum number of blocks to be allocated at once when a pool must be
719 /// replenished. If `maxBlocksPerChunk` is not specified, an
720 /// implementation-defined value is used. Optionally specify a
721 /// `basicAllocator` used to supply memory. If `basicAllocator` is 0,
722 /// the currently installed default allocator is used. Memory
723 /// allocation (and deallocation) requests will be satisfied using the
724 /// internally maintained pool managing memory blocks of the smallest
725 /// size not less than the requested size, or directly from the
726 /// underlying allocator (supplied at construction), if no internal pool
727 /// managing memory blocks of sufficient size exists. The behavior is
728 /// undefined unless `1 <= numPools` and `1 <= maxBlocksPerChunk`. Note
729 /// that, on platforms where
730 /// `8 < bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT`, excess memory may be
731 /// allocated for pools managing smaller blocks. Also note that
732 /// `maxBlocksPerChunk` need not be an integral power of 2; if geometric
733 /// growth would exceed the maximum value, the chunk size is capped at
734 /// that value.
736 bsls::BlockGrowth::Strategy growthStrategy,
737 int maxBlocksPerChunk,
738 bslma::Allocator *basicAllocator = 0);
739
741 const bsls::BlockGrowth::Strategy *growthStrategyArray,
742 bslma::Allocator *basicAllocator = 0);
744 const bsls::BlockGrowth::Strategy *growthStrategyArray,
745 int maxBlocksPerChunk,
746 bslma::Allocator *basicAllocator = 0);
748 bsls::BlockGrowth::Strategy growthStrategy,
749 const int *maxBlocksPerChunkArray,
750 bslma::Allocator *basicAllocator = 0);
751 /// Create a multipool allocator having the specified `numPools`,
752 /// indicating the number of internally created `bdlma::Pool` objects;
753 /// the block size of the first pool is 8 bytes, with the block size of
754 /// each additional pool successively doubling. Optionally specify a
755 /// `growthStrategy` indicating whether the number of blocks allocated
756 /// at once for every internally created `bdlma::Pool` should be either
757 /// fixed or grow geometrically, starting with 1. If `growthStrategy`
758 /// is not specified, optionally specify a `growthStrategyArray`,
759 /// indicating the strategies for each individual `bdlma::Pool` created
760 /// by this object. If neither `growthStrategy` nor
761 /// `growthStrategyArray` is specified, the allocation strategy for each
762 /// internally created `bdlma::Pool` object will grow geometrically,
763 /// starting from 1. Optionally specify a `maxBlocksPerChunk`,
764 /// indicating the maximum number of blocks to be allocated at once when
765 /// a pool must be replenished. If `maxBlocksPerChunk` is not
766 /// specified, optionally specify a `maxBlocksPerChunkArray`, indicating
767 /// the maximum number of blocks to allocate at once for each
768 /// individually created `bdlma::Pool` object. If neither
769 /// `maxBlocksPerChunk` nor `maxBlocksPerChunkArray` is specified, an
770 /// implementation-defined value is used. Optionally specify a
771 /// `basicAllocator` used to supply memory. If `basicAllocator` is 0,
772 /// the currently installed default allocator is used. Memory
773 /// allocation (and deallocation) requests will be satisfied using the
774 /// internally maintained pool managing memory blocks of the smallest
775 /// size not less than the requested size, or directly from the
776 /// underlying allocator (supplied at construction), if no internal pool
777 /// managing memory blocks of sufficient size exists. The behavior is
778 /// undefined unless `1 <= numPools`, `growthStrategyArray` has at least
779 /// `numPools` strategies, `1 <= maxBlocksPerChunk`, and
780 /// `maxBlocksPerChunkArray` has at least `numPools` positive values.
781 /// Note that, on platforms where
782 /// `8 < bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT`, excess memory may be
783 /// allocated for pools managing smaller blocks. Also note that the
784 /// maximum need not be an integral power of 2; if geometric growth
785 /// would exceed a maximum value, the chunk size is capped at that
786 /// value.
788 int numPools,
789 const bsls::BlockGrowth::Strategy *growthStrategyArray,
790 const int *maxBlocksPerChunkArray,
791 bslma::Allocator *basicAllocator = 0);
792
793 /// Destroy this multipool allocator. All memory allocated from this
794 /// allocator is released.
796
797 // MANIPULATORS
798
799 /// Reserve memory from this multipool allocator to satisfy memory
800 /// requests for at least the specified `numObjects` having the
801 /// specified `size` (in bytes) before the pool replenishes. If `size`
802 /// is 0, this method has no effect. The behavior is undefined unless
803 /// `size <= maxPooledBlockSize()` and `0 <= numObjects`.
804 void reserveCapacity(bsls::Types::size_type size, int numObjects);
805
806 // Virtual Functions
807
808 /// Return the address of a contiguous block of maximally-aligned memory
809 /// of (at least) the specified `size` (in bytes). If `size` is 0, no
810 /// memory is allocated and 0 is returned. If
811 /// `size > maxPooledBlockSize()`, the memory allocation is managed
812 /// directly by the underlying allocator, but will not be pooled .
813 void *allocate(bsls::Types::size_type size) BSLS_KEYWORD_OVERRIDE;
814
815 /// Return the memory block at the specified `address` back to this
816 /// allocator for reuse. If `address` is 0, this method has no effect.
817 /// The behavior is undefined unless `address` was allocated by this
818 /// allocator, and has not already been deallocated.
819 void deallocate(void *address) BSLS_KEYWORD_OVERRIDE;
820
821 /// Release all memory currently allocated through this multipool
822 /// allocator.
824
825 // ACCESSORS
826
827 /// Return the number of pools managed by this multipool allocator.
828 int numPools() const;
829
830 /// Return the maximum size of memory blocks that are pooled by this
831 /// multipool allocator. Note that the maximum value is defined as:
832 /// @code
833 /// 2 ^ (numPools + 2)
834 /// @endcode
835 /// where `numPools` is either specified at construction, or an
836 /// implementation-defined value.
837 bsls::Types::size_type maxPooledBlockSize() const;
838};
839
840// ============================================================================
841// INLINE DEFINITIONS
842// ============================================================================
843
844 // ------------------------
845 // class MultipoolAllocator
846 // ------------------------
847
848// CREATORS
849inline
851 bslma::Allocator *basicAllocator)
852: d_multipool(basicAllocator)
853{
854}
855
856inline
857MultipoolAllocator::MultipoolAllocator(
858 int numPools,
859 bslma::Allocator *basicAllocator)
860: d_multipool(numPools, basicAllocator)
861{
862}
863
864inline
865MultipoolAllocator::MultipoolAllocator(
866 bsls::BlockGrowth::Strategy growthStrategy,
867 bslma::Allocator *basicAllocator)
868: d_multipool(growthStrategy, basicAllocator)
869{
870}
871
872inline
873MultipoolAllocator::MultipoolAllocator(
874 int numPools,
875 bsls::BlockGrowth::Strategy growthStrategy,
876 bslma::Allocator *basicAllocator)
877: d_multipool(numPools, growthStrategy, basicAllocator)
878{
879}
880
881inline
882MultipoolAllocator::MultipoolAllocator(
883 int numPools,
884 const bsls::BlockGrowth::Strategy *growthStrategyArray,
885 bslma::Allocator *basicAllocator)
886: d_multipool(numPools, growthStrategyArray, basicAllocator)
887{
888}
889
890inline
891MultipoolAllocator::MultipoolAllocator(
892 int numPools,
893 bsls::BlockGrowth::Strategy growthStrategy,
894 int maxBlocksPerChunk,
895 bslma::Allocator *basicAllocator)
896: d_multipool(numPools, growthStrategy, maxBlocksPerChunk, basicAllocator)
897{
898}
899
900inline
901MultipoolAllocator::MultipoolAllocator(
902 int numPools,
903 const bsls::BlockGrowth::Strategy *growthStrategyArray,
904 int maxBlocksPerChunk,
905 bslma::Allocator *basicAllocator)
906: d_multipool(numPools, growthStrategyArray, maxBlocksPerChunk, basicAllocator)
907{
908}
909
910inline
911MultipoolAllocator::MultipoolAllocator(
912 int numPools,
913 bsls::BlockGrowth::Strategy growthStrategy,
914 const int *maxBlocksPerChunkArray,
915 bslma::Allocator *basicAllocator)
916: d_multipool(numPools, growthStrategy, maxBlocksPerChunkArray, basicAllocator)
917{
918}
919
920inline
921MultipoolAllocator::MultipoolAllocator(
922 int numPools,
923 const bsls::BlockGrowth::Strategy *growthStrategyArray,
924 const int *maxBlocksPerChunkArray,
925 bslma::Allocator *basicAllocator)
926: d_multipool(numPools,
927 growthStrategyArray,
928 maxBlocksPerChunkArray,
929 basicAllocator)
930{
931}
932
933// MANIPULATORS
934inline
936{
937 return d_multipool.allocate(size);
938}
939
940inline
942{
943 if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(address != 0)) {
944 d_multipool.deallocate(address);
945 }
946}
947
948inline
950{
951 d_multipool.release();
952}
953
954inline
956 int numObjects)
957{
958 d_multipool.reserveCapacity(size, numObjects);
959}
960
961// ACCESSORS
962inline
964{
965 return d_multipool.numPools();
966}
967
968inline
973
974} // close package namespace
975
976
977#endif
978
979// ----------------------------------------------------------------------------
980// Copyright 2016 Bloomberg Finance L.P.
981//
982// Licensed under the Apache License, Version 2.0 (the "License");
983// you may not use this file except in compliance with the License.
984// You may obtain a copy of the License at
985//
986// http://www.apache.org/licenses/LICENSE-2.0
987//
988// Unless required by applicable law or agreed to in writing, software
989// distributed under the License is distributed on an "AS IS" BASIS,
990// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
991// See the License for the specific language governing permissions and
992// limitations under the License.
993// ----------------------------- END-OF-FILE ----------------------------------
994
995/** @} */
996/** @} */
997/** @} */
Definition bdlma_managedallocator.h:391
Definition bdlma_multipoolallocator.h:682
bsls::Types::size_type maxPooledBlockSize() const
Definition bdlma_multipoolallocator.h:969
void * allocate(bsls::Types::size_type size) BSLS_KEYWORD_OVERRIDE
Definition bdlma_multipoolallocator.h:935
void deallocate(void *address) BSLS_KEYWORD_OVERRIDE
Definition bdlma_multipoolallocator.h:941
void reserveCapacity(bsls::Types::size_type size, int numObjects)
Definition bdlma_multipoolallocator.h:955
void release() BSLS_KEYWORD_OVERRIDE
Definition bdlma_multipoolallocator.h:949
~MultipoolAllocator() BSLS_KEYWORD_OVERRIDE
int numPools() const
Return the number of pools managed by this multipool allocator.
Definition bdlma_multipoolallocator.h:963
Definition bdlma_multipool.h:546
void release()
Relinquish all memory currently allocated via this multipool object.
int numPools() const
Return the number of pools managed by this multipool object.
Definition bdlma_multipool.h:812
void deallocate(void *address)
void reserveCapacity(bsls::Types::size_type size, int numBlocks)
bsls::Types::size_type maxPooledBlockSize() const
Definition bdlma_multipool.h:818
void * allocate(bsls::Types::size_type size)
Definition bslma_allocator.h:457
std::size_t size_type
Definition bslma_allocator.h:499
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
#define BSLS_KEYWORD_OVERRIDE
Definition bsls_keyword.h:653
#define BSLS_PERFORMANCEHINT_PREDICT_LIKELY(expr)
Definition bsls_performancehint.h:451
Definition bdlma_alignedallocator.h:276
Definition balxml_encoderoptions.h:68
Definition bdlt_iso8601util.h:691
Strategy
Definition bsls_blockgrowth.h:169
std::size_t size_type
Definition bsls_types.h:124