-
Notifications
You must be signed in to change notification settings - Fork 320
/
Copy pathbdlma_bufferedsequentialallocator.h
492 lines (456 loc) · 23 KB
/
bdlma_bufferedsequentialallocator.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
// bdlma_bufferedsequentialallocator.h -*-C++-*-
#ifndef INCLUDED_BDLMA_BUFFEREDSEQUENTIALALLOCATOR
#define INCLUDED_BDLMA_BUFFEREDSEQUENTIALALLOCATOR
#include <bsls_ident.h>
BSLS_IDENT("$Id: $")
//@PURPOSE: Provide an efficient managed allocator using an external buffer.
//
//@CLASSES:
// bdlma::BufferedSequentialAllocator: allocator using an external buffer
//
//@SEE_ALSO: bdlma_bufferedsequentialpool, 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::map's 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).
#include <bdlscm_version.h>
#include <bdlma_bufferedsequentialpool.h>
#include <bdlma_managedallocator.h>
#include <bslma_allocator.h>
#include <bsls_alignment.h>
#include <bsls_blockgrowth.h>
#include <bsls_performancehint.h>
#include <bsls_types.h>
namespace BloombergLP {
namespace bdlma {
// =================================
// class BufferedSequentialAllocator
// =================================
class BufferedSequentialAllocator : public ManagedAllocator {
// This class implements the 'ManagedAllocator' protocol to provide a fast
// allocator that dispenses heterogeneous blocks of memory (of varying,
// user-specified sizes) from an external buffer whose address and size (in
// bytes) are supplied at construction. If an allocation request exceeds
// the remaining free memory space in the external buffer, memory will be
// supplied by an (optional) allocator also supplied at construction; if no
// allocator is supplied, the currently installed default allocator is
// used. This class is *exception* *neutral*: If memory cannot be
// allocated, the behavior is defined by the (optional) allocator supplied
// at construction. Note that in no case will the buffered sequential
// allocator attempt to deallocate the external buffer.
// DATA
BufferedSequentialPool d_pool; // manager for allocated memory blocks
private:
// NOT IMPLEMENTED
BufferedSequentialAllocator(const BufferedSequentialAllocator&);
BufferedSequentialAllocator& operator=(const BufferedSequentialAllocator&);
public:
// CREATORS
BufferedSequentialAllocator(char *buffer,
bsls::Types::size_type size,
bslma::Allocator *basicAllocator = 0);
BufferedSequentialAllocator(
char *buffer,
bsls::Types::size_type size,
bsls::BlockGrowth::Strategy growthStrategy,
bslma::Allocator *basicAllocator = 0);
BufferedSequentialAllocator(char *buffer,
bsls::Types::size_type size,
bsls::Alignment::Strategy alignmentStrategy,
bslma::Allocator *basicAllocator = 0);
BufferedSequentialAllocator(
char *buffer,
bsls::Types::size_type size,
bsls::BlockGrowth::Strategy growthStrategy,
bsls::Alignment::Strategy alignmentStrategy,
bslma::Allocator *basicAllocator = 0);
// Create a buffered sequential allocator for allocating memory blocks
// from the specified external 'buffer' having the specified 'size' (in
// bytes). Optionally specify a 'growthStrategy' used to control
// buffer growth. If a 'growthStrategy' is not specified, geometric
// growth is used. Optionally specify an 'alignmentStrategy' used to
// align allocated memory blocks. If an 'alignmentStrategy' is not
// specified, natural alignment is used. Optionally specify a
// 'basicAllocator' used to supply memory should the capacity of
// 'buffer' be exhausted. If 'basicAllocator' is 0, the currently
// installed default allocator is used. The behavior is undefined
// unless '0 < size', and 'buffer' has at least 'size' bytes. Note
// that, due to alignment effects, it is possible that not all 'size'
// bytes of memory in 'buffer' can be used for allocation. Also note
// that no limit is imposed on the size of the internal buffers when
// geometric growth is used. Also note that when constant growth is
// used, the size of the internal buffers will always be the same as
// 'size'.
BufferedSequentialAllocator(char *buffer,
bsls::Types::size_type size,
bsls::Types::size_type maxBufferSize,
bslma::Allocator *basicAllocator = 0);
BufferedSequentialAllocator(
char *buffer,
bsls::Types::size_type size,
bsls::Types::size_type maxBufferSize,
bsls::BlockGrowth::Strategy growthStrategy,
bslma::Allocator *basicAllocator = 0);
BufferedSequentialAllocator(char *buffer,
bsls::Types::size_type size,
bsls::Types::size_type maxBufferSize,
bsls::Alignment::Strategy alignmentStrategy,
bslma::Allocator *basicAllocator = 0);
BufferedSequentialAllocator(
char *buffer,
bsls::Types::size_type size,
bsls::Types::size_type maxBufferSize,
bsls::BlockGrowth::Strategy growthStrategy,
bsls::Alignment::Strategy alignmentStrategy,
bslma::Allocator *basicAllocator = 0);
// Create a buffered sequential allocator for allocating memory blocks
// from the specified external 'buffer' having the specified 'size' (in
// bytes), or from an internal buffer (after the external 'buffer' is
// exhausted) where the buffer growth is limited to the specified
// 'maxBufferSize' (in bytes). Optionally specify a 'growthStrategy'
// used to control buffer growth. If a 'growthStrategy' is not
// specified, geometric growth is used. Optionally specify an
// 'alignmentStrategy' used to align allocated memory blocks. If an
// 'alignmentStrategy' is not specified, natural alignment is used.
// Optionally specify a 'basicAllocator' used to supply memory should
// the capacity of 'buffer' be exhausted. If 'basicAllocator' is 0,
// the currently installed default allocator is used. The behavior is
// undefined unless '0 < size', 'size <= maxBufferSize', and 'buffer'
// has at least 'size' bytes. Note that, due to alignment effects, it
// is possible that not all 'size' bytes of memory in 'buffer' can be
// used for allocation. Also note that when constant growth is used,
// the size of the internal buffers will always be the same as 'size'.
virtual ~BufferedSequentialAllocator();
// Destroy this buffered sequential allocator. All memory allocated
// from this allocator is released.
// MANIPULATORS
virtual void *allocate(size_type size);
// Return the address of a contiguous block of memory of the specified
// 'size' (in bytes) according to the alignment strategy specified at
// construction. If 'size' is 0, no memory is allocated and 0 is
// returned. If the allocation request exceeds the remaining free
// memory space in the external buffer supplied at construction, use
// memory obtained from the allocator supplied at construction.
virtual void deallocate(void *address);
// This method has no effect on the memory block at the specified
// 'address' as all memory allocated by this allocator is managed. The
// behavior is undefined unless 'address' is 0, or was allocated by
// this allocator and has not already been deallocated. The effect of
// using 'address' after this call is undefined.
virtual void release();
// Release all memory allocated through this allocator and return to
// the underlying allocator *all* memory except the external buffer
// supplied at construction. The allocator is reset to its
// default-constructed state, making the memory from the entire
// external buffer supplied at construction available for subsequent
// allocations, retaining the alignment and growth strategies, and the
// initial and maximum buffer sizes in effect following construction.
// The effect of subsequently - to this invokation of 'release' - using
// a pointer obtained from this object prior to this call to 'release'
// is undefined.
virtual void rewind();
// Release all memory allocated through this allocator and return to
// the underlying allocator *only* memory that was allocated outside of
// the typical internal buffer growth of this allocator (i.e., large
// blocks). All retained memory will be used to satisfy subsequent
// allocations. The effect of subsequently - to this invokation of
// 'rewind' - using a pointer obtained from this object prior to this
// call to 'rewind' is undefined.
};
// ============================================================================
// INLINE DEFINITIONS
// ============================================================================
// ---------------------------------
// class BufferedSequentialAllocator
// ---------------------------------
// CREATORS
inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
char *buffer,
bsls::Types::size_type size,
bslma::Allocator *basicAllocator)
: d_pool(buffer, size, basicAllocator)
{
}
inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
char *buffer,
bsls::Types::size_type size,
bsls::BlockGrowth::Strategy growthStrategy,
bslma::Allocator *basicAllocator)
: d_pool(buffer, size, growthStrategy, basicAllocator)
{
}
inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
char *buffer,
bsls::Types::size_type size,
bsls::Alignment::Strategy alignmentStrategy,
bslma::Allocator *basicAllocator)
: d_pool(buffer, size, alignmentStrategy, basicAllocator)
{
}
inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
char *buffer,
bsls::Types::size_type size,
bsls::BlockGrowth::Strategy growthStrategy,
bsls::Alignment::Strategy alignmentStrategy,
bslma::Allocator *basicAllocator)
: d_pool(buffer, size, growthStrategy, alignmentStrategy, basicAllocator)
{
}
inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
char *buffer,
bsls::Types::size_type size,
bsls::Types::size_type maxBufferSize,
bslma::Allocator *basicAllocator)
: d_pool(buffer, size, maxBufferSize, basicAllocator)
{
}
inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
char *buffer,
bsls::Types::size_type size,
bsls::Types::size_type maxBufferSize,
bsls::BlockGrowth::Strategy growthStrategy,
bslma::Allocator *basicAllocator)
: d_pool(buffer, size, maxBufferSize, growthStrategy, basicAllocator)
{
}
inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
char *buffer,
bsls::Types::size_type size,
bsls::Types::size_type maxBufferSize,
bsls::Alignment::Strategy alignmentStrategy,
bslma::Allocator *basicAllocator)
: d_pool(buffer, size, maxBufferSize, alignmentStrategy, basicAllocator)
{
}
inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
char *buffer,
bsls::Types::size_type size,
bsls::Types::size_type maxBufferSize,
bsls::BlockGrowth::Strategy growthStrategy,
bsls::Alignment::Strategy alignmentStrategy,
bslma::Allocator *basicAllocator)
: d_pool(buffer,
size,
maxBufferSize,
growthStrategy,
alignmentStrategy,
basicAllocator)
{
}
// MANIPULATORS
inline
void *BufferedSequentialAllocator::allocate(size_type size)
{
if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(0 == size)) {
BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
return 0; // RETURN
}
return d_pool.allocate(size);
}
inline
void BufferedSequentialAllocator::deallocate(void *)
{
}
inline
void BufferedSequentialAllocator::release()
{
d_pool.release();
}
inline
void BufferedSequentialAllocator::rewind()
{
d_pool.rewind();
}
} // close package namespace
} // close enterprise namespace
#endif
// ----------------------------------------------------------------------------
// Copyright 2016 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------