forked from boostorg/unordered
Merge branch 'develop' into feature/detect_reentrancy
This commit is contained in:
@@ -8,6 +8,10 @@
|
||||
|
||||
== Release 1.84.0
|
||||
|
||||
* Added `[c]visit_while` operations to `boost::concurrent_map`,
|
||||
with serial and parallel variants.
|
||||
* Added efficient move construction of `boost::unordered_flat_map` from
|
||||
`boost::concurrent_flat_map` and vice versa.
|
||||
* Added debug mode mechanisms for detecting illegal reentrancies into
|
||||
a `boost::concurrent_flat_map` from user code.
|
||||
|
||||
|
||||
@@ -154,7 +154,28 @@ m.visit_all(std::execution::par, [](auto& x) { // run in parallel
|
||||
});
|
||||
----
|
||||
|
||||
There is another whole-table visitation operation, `erase_if`:
|
||||
Traversal can be interrupted midway:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
// finds the key to a given (unique) value
|
||||
|
||||
int key = 0;
|
||||
int value = ...;
|
||||
bool found = !m.visit_while([&](const auto& x) {
|
||||
if(x.second == value) {
|
||||
key = x.first;
|
||||
return false; // finish
|
||||
}
|
||||
else {
|
||||
return true; // keep on visiting
|
||||
}
|
||||
});
|
||||
|
||||
if(found) { ... }
|
||||
----
|
||||
|
||||
There is one last whole-table visitation operation, `erase_if`:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
@@ -163,8 +184,8 @@ m.erase_if([](auto& x) {
|
||||
});
|
||||
----
|
||||
|
||||
`erase_if` can also be parallelized. Note that, in order to increase efficiency,
|
||||
these operations do not block the table during execution: this implies that elements
|
||||
`visit_while` and `erase_if` can also be parallelized. Note that, in order to increase efficiency,
|
||||
whole-table visitation operations do not block the table during execution: this implies that elements
|
||||
may be inserted, modified or erased by other threads during visitation. It is
|
||||
advisable not to assume too much about the exact global state of a `boost::concurrent_flat_map`
|
||||
at any point in your program.
|
||||
@@ -180,3 +201,29 @@ and the user need not take any special precaution, but overall performance may b
|
||||
Another blocking operation is _rehashing_, which happens explicitly via `rehash`/`reserve`
|
||||
or during insertion when the table's load hits `max_load()`. As with non-concurrent containers,
|
||||
reserving space in advance of bulk insertions will generally speed up the process.
|
||||
|
||||
== Interoperability with non-concurrent containers
|
||||
|
||||
As their internal data structure is basically the same, `boost::unordered_flat_map` can
|
||||
be efficiently move-constructed from `boost::concurrent_flat_map` and vice versa.
|
||||
This interoperability comes handy in multistage scenarios where parts of the data processing happen
|
||||
in parallel whereas other steps are non-concurrent (or non-modifying). In the following example,
|
||||
we want to construct a histogram from a huge input vector of words:
|
||||
the population phase can be done in parallel with `boost::concurrent_flat_map` and results
|
||||
then transferred to the final container.
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
std::vector<std::string> words = ...;
|
||||
|
||||
// Insert words in parallel
|
||||
boost::concurrent_flat_map<std::string_view, std::size_t> m0;
|
||||
std::for_each(
|
||||
std::execution::par, words.begin(), words.end(),
|
||||
[&](const auto& word) {
|
||||
m0.try_emplace_or_visit(word, 1, [](auto& x) { ++x.second; });
|
||||
});
|
||||
|
||||
// Transfer to a regular unordered_flat_map
|
||||
boost::unordered_flat_map m=std::move(m0);
|
||||
----
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace boost {
|
||||
explicit xref:#concurrent_flat_map_allocator_constructor[concurrent_flat_map](const Allocator& a);
|
||||
xref:#concurrent_flat_map_copy_constructor_with_allocator[concurrent_flat_map](const concurrent_flat_map& other, const Allocator& a);
|
||||
xref:#concurrent_flat_map_move_constructor_with_allocator[concurrent_flat_map](concurrent_flat_map&& other, const Allocator& a);
|
||||
xref:#concurrent_flat_map_move_constructor_from_unordered_flat_map[concurrent_flat_map](unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
xref:#concurrent_flat_map_initializer_list_constructor[concurrent_flat_map](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@@ -114,6 +115,16 @@ namespace boost {
|
||||
template<class ExecutionPolicy, class F>
|
||||
void xref:#concurrent_flat_map_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const;
|
||||
|
||||
template<class F> bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f);
|
||||
template<class F> bool xref:#concurrent_flat_map_cvisit_while[visit_while](F f) const;
|
||||
template<class F> bool xref:#concurrent_flat_map_cvisit_while[cvisit_while](F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_map_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_map_parallel_cvisit_while[cvisit_while](ExecutionPolicy&& policy, F f) const;
|
||||
|
||||
// capacity
|
||||
++[[nodiscard]]++ bool xref:#concurrent_flat_map_empty[empty]() const noexcept;
|
||||
size_type xref:#concurrent_flat_map_size[size]() const noexcept;
|
||||
@@ -503,6 +514,21 @@ Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from unordered_flat_map
|
||||
|
||||
```c++
|
||||
concurrent_flat_map(unordered_flat_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#unordered_flat_map[`unordered_flat_map`].
|
||||
The internal bucket array of `other` is transferred directly to the new container.
|
||||
The hash function, predicate and allocator are moved-constructed from `other`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; O(`bucket_count()`)
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
@@ -732,6 +758,50 @@ Unsequenced execution policies are not allowed.
|
||||
|
||||
---
|
||||
|
||||
==== [c]visit_while
|
||||
|
||||
```c++
|
||||
template<class F> bool visit_while(F f);
|
||||
template<class F> bool visit_while(F f) const;
|
||||
template<class F> bool cvisit_while(F f) const;
|
||||
```
|
||||
|
||||
Successively invokes `f` with references to each of the elements in the table until `f` returns `false`
|
||||
or all the elements are visited.
|
||||
Such references to the elements are const iff `*this` is const.
|
||||
|
||||
[horizontal]
|
||||
Returns:;; `false` iff `f` ever returns `false`.
|
||||
|
||||
---
|
||||
|
||||
==== Parallel [c]visit_while
|
||||
|
||||
```c++
|
||||
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const;
|
||||
```
|
||||
|
||||
Invokes `f` with references to each of the elements in the table until `f` returns `false`
|
||||
or all the elements are visited.
|
||||
Such references to the elements are const iff `*this` is const.
|
||||
Execution is parallelized according to the semantics of the execution policy specified.
|
||||
|
||||
[horizontal]
|
||||
Returns:;; `false` iff `f` ever returns `false`.
|
||||
Throws:;; Depending on the exception handling mechanism of the execution policy used, may call `std::terminate` if an exception is thrown within `f`.
|
||||
Notes:;; Only available in compilers supporting C++17 parallel algorithms. +
|
||||
+
|
||||
These overloads only participate in overload resolution if `std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>` is `true`. +
|
||||
+
|
||||
Unsequenced execution policies are not allowed. +
|
||||
+
|
||||
Parallelization implies that execution does not necessary finish as soon as `f` returns `false`, and as a result
|
||||
`f` may be invoked with further elements for which the return value is also `false`.
|
||||
|
||||
---
|
||||
|
||||
=== Size and Capacity
|
||||
|
||||
==== empty
|
||||
|
||||
@@ -77,6 +77,7 @@ namespace boost {
|
||||
explicit xref:#unordered_flat_map_allocator_constructor[unordered_flat_map](const Allocator& a);
|
||||
xref:#unordered_flat_map_copy_constructor_with_allocator[unordered_flat_map](const unordered_flat_map& other, const Allocator& a);
|
||||
xref:#unordered_flat_map_move_constructor_with_allocator[unordered_flat_map](unordered_flat_map&& other, const Allocator& a);
|
||||
xref:#unordered_flat_map_move_constructor_from_concurrent_flat_map[unordered_flat_map](concurrent_flat_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
xref:#unordered_flat_map_initializer_list_constructor[unordered_flat_map](std::initializer_list<value_type> il,
|
||||
size_type n = _implementation-defined_
|
||||
const hasher& hf = hasher(),
|
||||
@@ -472,6 +473,22 @@ from `other`, and the allocator is copy-constructed from `a`.
|
||||
|
||||
---
|
||||
|
||||
==== Move Constructor from concurrent_flat_map
|
||||
|
||||
```c++
|
||||
unordered_flat_map(concurrent_flat_map<Key, T, Hash, Pred, Allocator>&& other);
|
||||
```
|
||||
|
||||
Move construction from a xref:#concurrent_flat_map[`concurrent_flat_map`].
|
||||
The internal bucket array of `other` is transferred directly to the new container.
|
||||
The hash function, predicate and allocator are moved-constructed from `other`.
|
||||
|
||||
[horizontal]
|
||||
Complexity:;; Constant time.
|
||||
Concurrency:;; Blocking on `other`.
|
||||
|
||||
---
|
||||
|
||||
==== Initializer List Constructor
|
||||
[source,c++,subs="+quotes"]
|
||||
----
|
||||
|
||||
Reference in New Issue
Block a user