3#ifndef RPNXDATASTRUCTURES_SHARDED_MAP_HPP
4#define RPNXDATASTRUCTURES_SHARDED_MAP_HPP
11#include <unordered_map>
34 template <
typename Key,
typename Value,
typename Hash = std::hash< Key >,
typename KeyEqual = std::equal_to< Key >,
typename Alloc = std::allocator< std::pair< const Key, Value > > >
37 struct alignas(std::hardware_destructive_interference_size) shard
39 mutable std::mutex m_mutex;
40 std::unordered_map< Key, Value, Hash, KeyEqual, Alloc > m_map;
42 std::mutex& get_mutex()
const
47 shard(Alloc alloc) : m_map(0, Hash(), KeyEqual(), alloc)
51 shard(shard
const&) =
delete;
54 shard(shard&& other)
noexcept(std::is_nothrow_move_constructible_v<
decltype(m_map) >) : m_map(std::move(other.m_map))
61 std::vector< shard, typename std::allocator_traits< Alloc >::template rebind_alloc< shard > > m_shards;
63 template <
bool IsConst >
66 using map_type = std::conditional_t< IsConst, const conc_sharded_unordered_map, conc_sharded_unordered_map >;
67 using shard_type = std::conditional_t< IsConst, const shard, shard >;
68 using inner_iterator = std::conditional_t< IsConst, typename std::unordered_map< Key, Value, Hash, KeyEqual, Alloc >::const_iterator,
typename std::unordered_map< Key, Value, Hash, KeyEqual, Alloc >::iterator >;
71 std::size_t m_shard_index;
72 inner_iterator m_inner;
74 void advance_to_next_valid()
76 while (m_inner == m_map->m_shards[m_shard_index].m_map.end())
79 if (m_shard_index >= m_map->m_shards.size())
83 m_inner = m_map->m_shards[m_shard_index].m_map.begin();
88 using iterator_category = std::forward_iterator_tag;
89 using value_type = std::pair< const Key, Value >;
90 using difference_type = std::ptrdiff_t;
91 using pointer = std::conditional_t< IsConst, const value_type*, value_type* >;
92 using reference = std::conditional_t< IsConst, const value_type&, value_type& >;
94 basic_iterator() : m_map(
nullptr), m_shard_index(0), m_inner()
97 basic_iterator(map_type*
map, std::size_t shard_index, inner_iterator inner) : m_map(
map), m_shard_index(shard_index), m_inner(inner)
99 if (m_map && m_shard_index < m_map->m_shards.size())
101 advance_to_next_valid();
105 reference operator*()
const
109 pointer operator->()
const
114 basic_iterator& operator++()
117 advance_to_next_valid();
121 basic_iterator operator++(
int)
123 basic_iterator tmp = *
this;
128 [[nodiscard]]
bool operator==(
const basic_iterator& other)
const
130 if (m_map != other.m_map)
132 if (m_shard_index != other.m_shard_index)
134 if (m_shard_index >= (m_map ? m_map->m_shards.size() : 0))
136 return m_inner == other.m_inner;
139 bool operator!=(
const basic_iterator& other)
const
141 return !(*
this == other);
145 template <
typename Iterator >
152 return std::move(m_begin);
156 return std::move(m_end);
187 return {
iterator(
this, 0, m_shards[0].m_map.begin()),
iterator(
this, m_shards.size(), {})};
216 std::size_t total_size = 0;
217 for (
const auto& shard : m_shards)
219 std::lock_guard< std::mutex > lock(shard.get_mutex());
220 total_size += shard.m_map.size();
234 std::size_t total_size = 0;
235 for (
const auto& shard : m_shards)
237 total_size += shard.m_map.size();
249 explicit conc_sharded_unordered_map(std::size_t shard_count = std::thread::hardware_concurrency() * 2, Alloc
const& alloc = Alloc()) : m_shards((typename std::allocator_traits< Alloc >::template rebind_alloc< shard >)(alloc))
251 if (shard_count == 0 || (shard_count & (shard_count - 1)) != 0)
254 std::size_t power = 1;
255 while (power < shard_count)
261 m_shards.reserve(shard_count);
262 for (std::size_t i = 0; i < shard_count; ++i)
264 m_shards.emplace_back(alloc);
304 void put(Key
const& key, Value value)
306 std::size_t shard_index = Hash{}(key) & (m_shards.size() - 1);
307 shard& target_shard = m_shards[shard_index];
308 std::lock_guard< std::mutex > lock(target_shard.get_mutex());
309 target_shard.m_map[key] = std::move(value);
322 template <
typename Func >
325 std::size_t shard_index = Hash{}(key) & (m_shards.size() - 1);
326 shard& target_shard = m_shards[shard_index];
327 std::lock_guard< std::mutex > lock(target_shard.get_mutex());
328 target_shard.m_map[key] = func();
340 template <
typename Func >
343 std::size_t shard_index = Hash{}(key) & (m_shards.size() - 1);
344 shard& target_shard = m_shards[shard_index];
345 std::lock_guard< std::mutex > lock(target_shard.get_mutex());
346 if (target_shard.m_map.find(key) != target_shard.m_map.end())
350 target_shard.m_map[key] = func();
366 template <
typename Func >
369 std::size_t shard_index = Hash{}(key) & (m_shards.size() - 1);
370 shard& target_shard = m_shards[shard_index];
371 std::lock_guard< std::mutex > lock(target_shard.get_mutex());
372 if (
auto it = target_shard.m_map.find(key); it != target_shard.m_map.end())
378 auto [it2, inserted] = target_shard.m_map.emplace(key, func());
396 template <
typename Func >
399 std::size_t shard_index = Hash{}(key) & (m_shards.size() - 1);
400 shard& target_shard = m_shards[shard_index];
401 std::lock_guard< std::mutex > lock(target_shard.get_mutex());
402 if (
auto it = target_shard.m_map.find(key); it != target_shard.m_map.end())
410 auto& val = target_shard.m_map[key];
416 target_shard.m_map.erase(key);
435 template <
typename Func >
438 std::size_t shard_index = Hash{}(key) & (m_shards.size() - 1);
439 shard& target_shard = m_shards[shard_index];
440 std::lock_guard< std::mutex > lock(target_shard.get_mutex());
441 if (
auto it = target_shard.m_map.find(key); it != target_shard.m_map.end())
449 auto val = target_shard.m_map.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple());
450 func(val.first->first, val.first->second);
451 return val.first->second;
455 target_shard.m_map.erase(key);
471 std::size_t shard_index = Hash{}(key) & (m_shards.size() - 1);
472 shard& target_shard = m_shards[shard_index];
473 std::lock_guard< std::mutex > lock(target_shard.get_mutex());
474 auto [it, inserted] = target_shard.m_map.emplace(key, std::move(value));
489 std::size_t shard_index = Hash{}(key) & (m_shards.size() - 1);
490 shard& target_shard = m_shards[shard_index];
491 std::lock_guard< std::mutex > lock(target_shard.get_mutex());
492 return target_shard.m_map.at(key);
505 std::size_t shard_index = Hash{}(key) & (m_shards.size() - 1);
506 shard& target_shard = m_shards[shard_index];
507 std::lock_guard< std::mutex > lock(target_shard.get_mutex());
508 target_shard.m_map.erase(key);
range< const_iterator > range_exclusive() const
Definition sharded_unordered_map.hpp:198
Value & get_or_create(Key const &key, Func func)
Definition sharded_unordered_map.hpp:367
conc_sharded_unordered_map(conc_sharded_unordered_map &&)=delete
bool try_put(Key const &key, Value value)
Definition sharded_unordered_map.hpp:469
void put(Key const &key, Value value)
Definition sharded_unordered_map.hpp:304
Value & get_or_init_iter(Key const &key, Func func)
Definition sharded_unordered_map.hpp:436
basic_iterator< true > const_iterator
Definition sharded_unordered_map.hpp:175
std::size_t size_exclusive() const
Definition sharded_unordered_map.hpp:232
conc_sharded_unordered_map & operator=(conc_sharded_unordered_map const &)=delete
range< iterator > range_exclusive()
Definition sharded_unordered_map.hpp:185
basic_iterator< false > iterator
Definition sharded_unordered_map.hpp:167
void erase(Key const &key)
Definition sharded_unordered_map.hpp:503
std::size_t estimate_size() const
Definition sharded_unordered_map.hpp:214
conc_sharded_unordered_map(std::size_t shard_count=std::thread::hardware_concurrency() *2, Alloc const &alloc=Alloc())
Definition sharded_unordered_map.hpp:249
void put_exec(Key const &key, Func func)
Definition sharded_unordered_map.hpp:323
Value get(Key const &key)
Definition sharded_unordered_map.hpp:487
conc_sharded_unordered_map(conc_sharded_unordered_map const &)=delete
conc_sharded_unordered_map & operator=(conc_sharded_unordered_map &&)=delete
Value & get_or_init(Key const &key, Func func)
Definition sharded_unordered_map.hpp:397
bool try_put_exec(Key const &key, Func func)
Definition sharded_unordered_map.hpp:341
~conc_sharded_unordered_map()=default
Ordered key-value container with size-first ordering.
Definition map.hpp:31
Containers, iterator adapters, callable wrappers, and value utilities.
Definition annex.hpp:14
bool operator!=(annex< T, Alloc > const &lhs, annex< T, Alloc > const &rhs)
Compares two annex objects for inequality.
Definition annex.hpp:615
bool operator==(annex< T, Alloc > const &lhs, annex< T, Alloc > const &rhs)
Compares two annex objects for equality.
Definition annex.hpp:598