Reworked devector's relocation options, instead of relocation_limit<relocation_limit_XX>, it's specified by single "relocate_on_XX"

This commit is contained in:
Ion Gaztañaga committed 2022-10-01 23:39:28 +02:00
1 parent 4e4d2afef2
commit 4bed49ee14
9 files changed
+819 -103

No files matched your search

+105 -33
View File
@@ -471,7 +471,49 @@ implementation of methods that modify the devector at the front. In general, `de
are a superset of those of `vector` with similar behaviour, barring a couple of iterator invalidation
guarantees that differ.
The overhead for devector is one extra `size_t` per container: Usually sizeof(devector) == 4*sizeof(T*).
The static size overhead for boost's devector is one extra `size_t` per container: Usually sizeof(devector) == 4*sizeof(T*).
There are different strategies when elements are to be inserted at one extreme of the container and there is
no room for additional elements at that extreme. One simple strategy would be to reallocate a bigger buffer
and move all elements to the new memory. However, this would lead to unbounded memory waste when elements are
inserted predominantly on one extreme (e.g. pushed at one extreme and popped from the other, like a LIFO pattern).
To avoid unbounded memory waste, Boost.Container's `devector` uses a different strategy:
* If elements are inserted near a extreme and there is free space on that extreme, the insertion is performed
without any additional data movement (only the elements between the insertion point and the extreme are moved.
* If elements are inserted near one extreme and the free space on that extreme is exhausted, all existing elements
are relocated (moved) to the center of the internal memory buffer. This makes room in the exhausted extreme
to insert more elements whihout allocating a new buffer.
* Potentially, the maximum number of possible relocations (movements) reusing the memory buffer are Olog(N),
but that would lead non-amortized constant-time insertion at the extremes. In consequence, the number of
relocations must be limited ('relocation limit') and a reallocation (allocation of a new memory buffer) will be
performed if the load-factor of the container defined as (size()/length_of_buffer) surpasses the relocation limit (see
Lars Greger Nordland Hagen's [href http://larshagencpp.github.io/blog/2016/05/22/devector"Double-ended vector - is it useful?"]
article for more details.
* This approach offers a reasonable balance between a reasonable memory overhead and performance.
However, this strategy has also some downsides:
* Insertions at the extremes have no strong exception guarantee as data has to be move inside the existing buffer.
* Due to the memory relocation vs reallocation strategy explained above:
* `capacity()` can no longer tell the maximum number of elements that the container can hold and, at the same time,
the number of insertions to perform before a reallocation is performed. Depending on which extreme a insertion
takes place, a reallocation might occur or not (maybe there is free capacity at that extreme)
* Instead of removing the `capacity()` member or renaming it to "minimum_capacity()", the definition has been changed
to tell the *minimum* number of elements that can be inserted without reallocating. This allows the typical pattern
where:
* If `reserve(n)` is called, `capacity() >= n`
* If `capacity() == n` it is guaranteed that if `size() <= n` no reallocation will occur.
* However the usual container invariant where `size() <= capacity()` does not hold. `size()` can be bigger than
`capacity()` because elements can be always inserted at a extreme with free capacity without reallocation.
[endsect]
@@ -685,7 +727,7 @@ used to customize these containers:
[endsect]
[section:configurable_vectors Configurable vectors]
[section:configurable_vector Configurable vector]
The configuration for [classref boost::container::vector vector] is passed as
the last template parameter and defined using the utility class
@@ -706,42 +748,14 @@ the last template parameter and defined using the utility class
`size()` and `capacity()` inside vector, so that the size of an empty vector is minimized and cache
performance might be improved. See [classref boost::container::stored_size stored_size] for more details.
* [classref boost::container::devector devector] supports an addicional [classref boost::container::relocation_limit relocation_limit]
options to control container's behaviour when back or front free capacity is exhausted.
See the following example to see how [classref boost::container::vector_options vector_options] can be
used to customize `vector` container:
used to customize `vector`:
[import ../example/doc_custom_vector.cpp]
[doc_custom_vector]
[endsect]
[section:configurable_deques Configurable deques]
The configuration for [classref boost::container::deque deque] is passed as
the last template parameter and defined using the utility class
[classref boost::container::deque_options deque_options]. The following parameters can be configured:
Parameters that control the size of deque's 'block' (deque allocates contiguous chunks of elements, called 'blocks').
Only one of these paratemers can be specified:
* [classref boost::container::block_bytes block_bytes]: the number of bytes deque will allocate for store
elements contiguously: `deque::get_block_size()` will return aproximately `block_bytes/sizeof(value_type)`.
A value of zero means the default value.
* [classref boost::container::block_size block_size]: the number of elements deque will allocate contiguously.
If this option is specified, `deque::get_block_size()` will return the specified `block_size`.
A value of zero means the default value.
See the following example to see how [classref boost::container::deque_options deque_options] can be
used to customize `deque` container:
[import ../example/doc_custom_deque.cpp]
[doc_custom_deque]
[endsect]
[section:configurable_static_vectors Configurable static vector]
The configuration for [classref boost::container::static_vector static_vector] is passed as
@@ -757,7 +771,7 @@ the last template parameter and defined using the utility class
does not throw or abort, undefined behavior is triggered.
See the following example to see how [classref boost::container::static_vector_options static_vector_options] can be
used to customize `static_vector` container:
used to customize `static_vector`:
[import ../example/doc_custom_static_vector.cpp]
[doc_custom_static_vector]
@@ -784,13 +798,71 @@ the last template parameter and defined using the utility class
[classref boost::container::growth_factor_50 growth_factor_100].
See the following example to see how [classref boost::container::small_vector_options small_vector_options] can be
used to customize `small_vector` container:
used to customize `small_vector`:
[import ../example/doc_custom_small_vector.cpp]
[doc_custom_small_vector]
[endsect]
[section:configurable_deques Configurable deques]
The configuration for [classref boost::container::deque deque] is passed as
the last template parameter and defined using the utility class
[classref boost::container::deque_options deque_options]. The following parameters can be configured:
Parameters that control the size of deque's 'block' (deque allocates contiguous chunks of elements, called 'blocks').
Only one of these paratemers can be specified:
* [classref boost::container::block_bytes block_bytes]: the number of bytes deque will allocate for store
elements contiguously: `deque::get_block_size()` will return aproximately `block_bytes/sizeof(value_type)`.
A value of zero means the default value.
* [classref boost::container::block_size block_size]: the number of elements deque will allocate contiguously.
If this option is specified, `deque::get_block_size()` will return the specified `block_size`.
A value of zero means the default value.
See the following example to see how [classref boost::container::deque_options deque_options] can be
used to customize `deque`:
[import ../example/doc_custom_deque.cpp]
[doc_custom_deque]
[endsect]
[section:configurable_devector Configurable devector]
The configuration for [classref boost::container::devector devector] is passed as
the last template parameter and defined using the utility class
[classref boost::container::devector_options devector_options]. The following parameters can be configured:
* [classref boost::container::growth_factor growth_factor]: the growth policy of the devector.
The rate at which the capacity of a devector grows is implementation dependent and
implementations choose exponential growth in order to meet the amortized constant time requirement for push_back.
A higher growth factor will make it faster as it will require less data movement, but it will have a greater memory
impact (on average, more memory will be unused). A user can provide a custom implementation of the growth factor and some
predefined policies are available: [classref boost::container::growth_factor_50 growth_factor_50],
[classref boost::container::growth_factor_60 growth_factor_60] (usually the default) and
[classref boost::container::growth_factor_100 growth_factor_100].
* [classref boost::container::stored_size stored_size]: the type that will be used to store size-related
parameters inside of the devector. Sometimes, when the maximum capacity to be used is much less than the
theoretical maximum that a devector can hold, it's interesting to use smaller unsigned integer types to represent
`size()` and `capacity()` inside devector, so that the size of an empty devector is minimized and cache
performance might be improved. See [classref boost::container::stored_size stored_size] for more details.
* [classref boost::container::relocate_on_66 relocate_on_XX]: load factor limit that will determine if
new memory should be allocated or elements should relocated inside existing memory, when the free space
is exhausted at one end of the container.
See the following example to see how [classref boost::container::devector_options devector_options] can be
used to customize `devector`:
[import ../example/doc_custom_devector.cpp]
[doc_custom_devector]
[endsect]
[endsect]
[section:extended_allocators Extended functionality: Extended allocators]