Feature/bulk visit (#217)
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 25 KiB |
@@ -447,6 +447,10 @@ operations follow a https://en.wikipedia.org/wiki/Zipf%27s_law#Formal_definition
|
||||
with different _skew_ parameters: the higher the skew, the more concentrated are the keys in the lower values
|
||||
of the covered range.
|
||||
|
||||
`boost::concurrent_flat_map` is exercised using both regular and xref:#concurrent_bulk_visitation[bulk visitation]:
|
||||
in the latter case, lookup keys are buffered in a local array and then processed at
|
||||
once each time the buffer reaches xref:#concurrent_flat_map_constants[`bulk_visit_size`].
|
||||
|
||||
=== GCC 12, x64
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
with serial and parallel variants.
|
||||
* Added efficient move construction of `boost::unordered_flat_(map|set)` from
|
||||
`boost::concurrent_flat_(map|set)` and vice versa.
|
||||
* Added bulk visitation to concurrent containers for increased lookup performance.
|
||||
* Added debug-mode mechanisms for detecting illegal reentrancies into
|
||||
a concurrent container from user code.
|
||||
* Added Boost.Serialization support to all containers and their (non-local) iterator types.
|
||||
|
||||
@@ -194,6 +194,55 @@ 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 concurrent container
|
||||
at any point in your program.
|
||||
|
||||
== Bulk visitation
|
||||
|
||||
Suppose you have an `std::array` of keys you want to look up for in a concurrent map:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
std::array<int, N> keys;
|
||||
...
|
||||
for(const auto& key: keys) {
|
||||
m.visit(key, [](auto& x) { ++x.second; });
|
||||
}
|
||||
----
|
||||
|
||||
_Bulk visitation_ allows us to pass all the keys in one operation:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
m.visit(keys.begin(), keys.end(), [](auto& x) { ++x.second; });
|
||||
----
|
||||
|
||||
This functionality is not provided for mere syntactic convenience, though: by processing all the
|
||||
keys at once, some internal optimizations can be applied that increase
|
||||
performance over the regular, one-at-a-time case (consult the
|
||||
xref:#benchmarks_boostconcurrent_flat_map[benchmarks]). In fact, it may be beneficial
|
||||
to buffer incoming keys so that they can be bulk visited in chunks:
|
||||
|
||||
[source,c++]
|
||||
----
|
||||
static constexpr auto bulk_visit_size = boost::concurrent_flat_map<int,int>::bulk_visit_size;
|
||||
std::array<int, bulk_visit_size> buffer;
|
||||
std::size_t i=0;
|
||||
while(...) { // processing loop
|
||||
...
|
||||
buffer[i++] = k;
|
||||
if(i == bulk_visit_size) {
|
||||
map.visit(buffer.begin(), buffer.end(), [](auto& x) { ++x.second; });
|
||||
i = 0;
|
||||
}
|
||||
...
|
||||
}
|
||||
// flush remaining keys
|
||||
map.visit(buffer.begin(), buffer.begin() + i, [](auto& x) { ++x.second; });
|
||||
----
|
||||
|
||||
There's a latency/throughput tradeoff here: it will take longer for incoming keys to
|
||||
be processed (since they are buffered), but the number of processed keys per second
|
||||
is higher. `bulk_visit_size` is the recommended chunk size —smaller buffers
|
||||
may yield worse performance.
|
||||
|
||||
== Blocking Operations
|
||||
|
||||
``boost::concurrent_flat_set``s and ``boost::concurrent_flat_map``s can be copied, assigned, cleared and merged just like any
|
||||
|
||||
@@ -50,6 +50,9 @@ namespace boost {
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
// constants
|
||||
static constexpr size_type xref:#concurrent_flat_map_constants[bulk_visit_size] = _implementation-defined_;
|
||||
|
||||
// construct/copy/destroy
|
||||
xref:#concurrent_flat_map_default_constructor[concurrent_flat_map]();
|
||||
explicit xref:#concurrent_flat_map_bucket_count_constructor[concurrent_flat_map](size_type n,
|
||||
@@ -106,6 +109,13 @@ namespace boost {
|
||||
template<class K, class F> size_t xref:#concurrent_flat_map_cvisit[visit](const K& k, F f) const;
|
||||
template<class K, class F> size_t xref:#concurrent_flat_map_cvisit[cvisit](const K& k, F f) const;
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_map_bulk_visit[visit](FwdIterator first, FwdIterator last, F f);
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_map_bulk_visit[visit](FwdIterator first, FwdIterator last, F f) const;
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_map_bulk_visit[cvisit](FwdIterator first, FwdIterator last, F f) const;
|
||||
|
||||
template<class F> size_t xref:#concurrent_flat_map_cvisit_all[visit_all](F f);
|
||||
template<class F> size_t xref:#concurrent_flat_map_cvisit_all[visit_all](F f) const;
|
||||
template<class F> size_t xref:#concurrent_flat_map_cvisit_all[cvisit_all](F f) const;
|
||||
@@ -386,6 +396,13 @@ a function visiting elements of `m`) are detected and signalled through `BOOST_A
|
||||
When run-time speed is a concern, the feature can be disabled by globally defining
|
||||
this macro.
|
||||
|
||||
=== Constants
|
||||
|
||||
```cpp
|
||||
static constexpr size_type bulk_visit_size;
|
||||
```
|
||||
|
||||
Chunk size internally used in xref:concurrent_flat_map_bulk_visit[bulk visit] operations.
|
||||
|
||||
=== Constructors
|
||||
|
||||
@@ -722,6 +739,42 @@ Notes:;; The `template<class K, class F>` overloads only participate in overload
|
||||
|
||||
---
|
||||
|
||||
==== Bulk visit
|
||||
|
||||
```c++
|
||||
template<class FwdIterator, class F>
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f);
|
||||
template<class FwdIterator, class F>
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f) const;
|
||||
template<class FwdIterator, class F>
|
||||
size_t cvisit(FwdIterator first, FwdIterator last, F f) const;
|
||||
```
|
||||
|
||||
For each element `k` in the range [`first`, `last`),
|
||||
if there is an element `x` in the container with key equivalent to `k`,
|
||||
invokes `f` with a reference to `x`.
|
||||
Such reference is const iff `*this` is const.
|
||||
|
||||
Although functionally equivalent to individually invoking
|
||||
xref:concurrent_flat_map_cvisit[`[c\]visit`] for each key, bulk visitation
|
||||
performs generally faster due to internal streamlining optimizations.
|
||||
It is advisable that `std::distance(first,last)` be at least
|
||||
xref:#concurrent_flat_map_constants[`bulk_visit_size`] to enjoy
|
||||
a performance gain: beyond this size, performance is not expected
|
||||
to increase further.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `FwdIterator` is a https://en.cppreference.com/w/cpp/named_req/ForwardIterator[LegacyForwardIterator^]
|
||||
({cpp}11 to {cpp}17),
|
||||
or satisfies https://en.cppreference.com/w/cpp/iterator/forward_iterator[std::forward_iterator^] ({cpp}20 and later).
|
||||
For `K` = `std::iterator_traits<FwdIterator>::value_type`, either `K` is `key_type` or
|
||||
else `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs.
|
||||
In the latter case, the library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent.
|
||||
This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
Returns:;; The number of elements visited.
|
||||
|
||||
---
|
||||
|
||||
==== [c]visit_all
|
||||
|
||||
```c++
|
||||
|
||||
@@ -45,6 +45,9 @@ namespace boost {
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
// constants
|
||||
static constexpr size_type xref:#concurrent_flat_set_constants[bulk_visit_size] = _implementation-defined_;
|
||||
|
||||
// construct/copy/destroy
|
||||
xref:#concurrent_flat_set_default_constructor[concurrent_flat_set]();
|
||||
explicit xref:#concurrent_flat_set_bucket_count_constructor[concurrent_flat_set](size_type n,
|
||||
@@ -98,6 +101,11 @@ namespace boost {
|
||||
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[visit](const K& k, F f) const;
|
||||
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[cvisit](const K& k, F f) const;
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f) const;
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_set_bulk_visit[cvisit](FwdIterator first, FwdIterator last, F f) const;
|
||||
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[visit_all](F f) const;
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[cvisit_all](F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
@@ -340,6 +348,13 @@ a function visiting elements of `m`) are detected and signalled through `BOOST_A
|
||||
When run-time speed is a concern, the feature can be disabled by globally defining
|
||||
this macro.
|
||||
|
||||
=== Constants
|
||||
|
||||
```cpp
|
||||
static constexpr size_type bulk_visit_size;
|
||||
```
|
||||
|
||||
Chunk size internally used in xref:concurrent_flat_set_bulk_visit[bulk visit] operations.
|
||||
|
||||
=== Constructors
|
||||
|
||||
@@ -672,6 +687,39 @@ Notes:;; The `template<class K, class F>` overloads only participate in overload
|
||||
|
||||
---
|
||||
|
||||
==== Bulk visit
|
||||
|
||||
```c++
|
||||
template<class FwdIterator, class F>
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f) const;
|
||||
template<class FwdIterator, class F>
|
||||
size_t cvisit(FwdIterator first, FwdIterator last, F f) const;
|
||||
```
|
||||
|
||||
For each element `k` in the range [`first`, `last`),
|
||||
if there is an element `x` in the container with key equivalent to `k`,
|
||||
invokes `f` with a const reference to `x`.
|
||||
|
||||
Although functionally equivalent to individually invoking
|
||||
xref:concurrent_flat_set_cvisit[`[c\]visit`] for each key, bulk visitation
|
||||
performs generally faster due to internal streamlining optimizations.
|
||||
It is advisable that `std::distance(first,last)` be at least
|
||||
xref:#concurrent_flat_set_constants[`bulk_visit_size`] to enjoy
|
||||
a performance gain: beyond this size, performance is not expected
|
||||
to increase further.
|
||||
|
||||
[horizontal]
|
||||
Requires:;; `FwdIterator` is a https://en.cppreference.com/w/cpp/named_req/ForwardIterator[LegacyForwardIterator^]
|
||||
({cpp}11 to {cpp}17),
|
||||
or satisfies https://en.cppreference.com/w/cpp/iterator/forward_iterator[std::forward_iterator^] ({cpp}20 and later).
|
||||
For `K` = `std::iterator_traits<FwdIterator>::value_type`, either `K` is `key_type` or
|
||||
else `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs.
|
||||
In the latter case, the library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent.
|
||||
This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
|
||||
Returns:;; The number of elements visited.
|
||||
|
||||
---
|
||||
|
||||
==== [c]visit_all
|
||||
|
||||
```c++
|
||||
|
||||