5 template<
class,
class,
class>
7 template<
class,
class,
class>
9 template<
class,
class,
class>
10 class ConstraintPolicy,
11 typename MeasureValue,
15 template<
typename... Args>
17 : m_insertion_policy(std::make_unique<MyInsertionPolicy>()),
18 m_eviction_policy(std::make_unique<MyEvictionPolicy>()),
19 m_constraint_policy(std::make_unique<MyConstraintPolicy>(std::forward<Args>(args)...)),
22 m_hit_rate_acc(boost::accumulators::tag::rolling_window::window_size = m_statistics_window_size),
23 m_byte_hit_rate_acc(boost::accumulators::tag::rolling_window::window_size = m_statistics_window_size)
29 template<
class,
class,
class>
31 template<
class,
class,
class>
33 template<
class,
class,
class>
39 template<
typename Coll,
typename... Args>
41 : m_insertion_policy(std::make_unique<MyInsertionPolicy>()),
42 m_eviction_policy(std::make_unique<MyEvictionPolicy>()),
43 m_constraint_policy(std::move(
44 std::apply([](auto&&... params) {
return std::make_unique<MyConstraintPolicy>(std::forward<decltype(params)>(params)...); }, std::move(args)))),
47 m_hit_rate_acc(boost::accumulators::tag::rolling_window::window_size = m_statistics_window_size),
48 m_byte_hit_rate_acc(boost::accumulators::tag::rolling_window::window_size = m_statistics_window_size)
55 template<
class,
class,
class>
57 template<
class,
class,
class>
59 template<
class,
class,
class>
65 template<
typename KeyView>
68 LockGuard guard(lock());
69 return m_data.find(key) != m_data.end();
74 template<
class,
class,
class>
76 template<
class,
class,
class>
78 template<
class,
class,
class>
84 template<
typename KeyView>
87 LockGuard guard(lock());
89 auto key_and_item = m_data.find(key);
90 if (key_and_item != m_data.end()) {
91 on_cache_hit(key_and_item->first, key_and_item->second);
92 return key_and_item->second.m_value;
101 template<
class,
class,
class>
103 template<
class,
class,
class>
105 template<
class,
class,
class>
111 template<
class Container>
114 using namespace detail;
117 constexpr
auto emplace_fn = boost::hana::if_(
118 traits::stl::has_emplace_back<Container, K, V>,
119 [](
auto& seq_container,
const auto& key,
const auto& item) { seq_container.emplace_back(key, item.m_value); },
120 [](
auto& assoc_container,
const auto& key,
const auto& item) { assoc_container.emplace(key, item.m_value); });
122 LockGuard guard(lock());
126 boost::hana::and_(traits::stl::has_reserve<Container>, traits::stl::has_size<Container>),
127 [&](
auto& c) { c.reserve(c.size() + m_data.size()); },
128 [](
auto&) {})(container);
131 for (
const auto& [key, cached_item] : m_data) {
132 emplace_fn(container, key, cached_item);
138 template<
class,
class,
class>
140 template<
class,
class,
class>
142 template<
class,
class,
class>
150 LockGuard guard(lock());
152 const auto key_size =
static_cast<size_t>(m_measure_key(key));
153 const auto value_size =
static_cast<size_t>(m_measure_value(value));
155 CacheItem new_item{key_size, std::move(value), value_size};
157 auto it = m_data.find(key);
158 if (it != m_data.end()) {
159 if (check_replace(key, it->second, new_item)) {
161 insert_or_update(std::move(key), std::move(new_item));
165 if (check_insert(key, new_item)) {
166 const auto it_and_ok = m_data.insert_or_assign(std::move(key), std::move(new_item));
167 assert(it_and_ok.second);
169 on_insert(it_and_ok.first->first, it_and_ok.first->second);
179 template<
class,
class,
class>
181 template<
class,
class,
class>
183 template<
class,
class,
class>
191 LockGuard guard(lock());
193 auto key_and_item = m_data.find(key);
194 if (key_and_item != m_data.end()) {
195 remove(key_and_item);
203 template<
class,
class,
class>
205 template<
class,
class,
class>
207 template<
class,
class,
class>
215 LockGuard guard(lock());
219 m_hit_rate_acc = MeanAccumulator(boost::accumulators::tag::rolling_window::window_size = m_statistics_window_size);
220 m_byte_hit_rate_acc = MeanAccumulator(boost::accumulators::tag::rolling_window::window_size = m_statistics_window_size);
222 m_insertion_policy->clear();
223 m_eviction_policy->clear();
224 m_constraint_policy->clear();
229 template<
class,
class,
class>
231 template<
class,
class,
class>
233 template<
class,
class,
class>
242 LockGuard guard(lock());
244 for (
auto it = m_data.begin(); it != m_data.end();) {
247 if (!predicate_fn(it->first, item.
m_value)) {
257 template<
class,
class,
class>
259 template<
class,
class,
class>
261 template<
class,
class,
class>
270 LockGuard guard(lock());
271 for (
const auto& [key, value] : m_data) {
272 unary_function(key, value.m_value);
278 template<
class,
class,
class>
280 template<
class,
class,
class>
282 template<
class,
class,
class>
292 std::pair<LockGuard, LockGuard> guards{lock_pair(other)};
296 swap(m_statistics_window_size, other.m_statistics_window_size);
298 swap(m_insertion_policy, other.m_insertion_policy);
299 swap(m_eviction_policy, other.m_eviction_policy);
300 swap(m_constraint_policy, other.m_constraint_policy);
302 swap(m_data, other.m_data);
304 swap(m_hit_rate_acc, other.m_hit_rate_acc);
305 swap(m_byte_hit_rate_acc, other.m_byte_hit_rate_acc);
306 }
catch (
const std::system_error& e) {
316 assert(e.code() == std::errc::operation_not_permitted || e.code() == std::errc::resource_deadlock_would_occur);
326 template<
class,
class,
class>
328 template<
class,
class,
class>
330 template<
class,
class,
class>
338 LockGuard guard(lock());
339 return m_data.size();
344 template<
class,
class,
class>
346 template<
class,
class,
class>
348 template<
class,
class,
class>
354 template<
typename... Args>
357 LockGuard guard(lock());
358 m_constraint_policy->update(std::forward<Args>(args)...);
360 auto should_evict_another = [&](
auto victim_it) {
return victim_it != m_eviction_policy->victim_end() && !m_constraint_policy->is_satisfied(); };
362 for (
auto victim_it = m_eviction_policy->victim_begin(); should_evict_another(victim_it); victim_it = m_eviction_policy->victim_begin()) {
363 const K& key_to_evict = *victim_it;
364 auto key_and_item = m_data.find(key_to_evict);
366 if (key_and_item != m_data.end()) {
367 remove(key_and_item);
374 assert(m_constraint_policy->is_satisfied());
379 template<
class,
class,
class>
381 template<
class,
class,
class>
383 template<
class,
class,
class>
391 return *m_insertion_policy;
396 template<
class,
class,
class>
398 template<
class,
class,
class>
400 template<
class,
class,
class>
408 return *m_insertion_policy;
413 template<
class,
class,
class>
415 template<
class,
class,
class>
417 template<
class,
class,
class>
425 return *m_eviction_policy;
430 template<
class,
class,
class>
432 template<
class,
class,
class>
434 template<
class,
class,
class>
442 return *m_eviction_policy;
447 template<
class,
class,
class>
449 template<
class,
class,
class>
451 template<
class,
class,
class>
459 return *m_constraint_policy;
464 template<
class,
class,
class>
466 template<
class,
class,
class>
468 template<
class,
class,
class>
476 return *m_constraint_policy;
481 template<
class,
class,
class>
483 template<
class,
class,
class>
485 template<
class,
class,
class>
493 return boost::accumulators::rolling_mean(m_hit_rate_acc);
498 template<
class,
class,
class>
500 template<
class,
class,
class>
502 template<
class,
class,
class>
510 return boost::accumulators::rolling_mean(m_byte_hit_rate_acc);
515 template<
class,
class,
class>
517 template<
class,
class,
class>
519 template<
class,
class,
class>
527 return m_statistics_window_size;
532 template<
class,
class,
class>
534 template<
class,
class,
class>
536 template<
class,
class,
class>
544 m_statistics_window_size = window_size;
546 m_hit_rate_acc = MeanAccumulator(boost::accumulators::tag::rolling_window::window_size = m_statistics_window_size);
547 m_byte_hit_rate_acc = MeanAccumulator(boost::accumulators::tag::rolling_window::window_size = m_statistics_window_size);
552 template<
class,
class,
class>
554 template<
class,
class,
class>
556 template<
class,
class,
class>
565 LockGuard guard{m_mutex};
575 template<
class,
class,
class>
577 template<
class,
class,
class>
579 template<
class,
class,
class>
585 auto Cache<K, V, I, E, C, SV, SK, KH, TS>::lock([[maybe_unused]] std::defer_lock_t defer_lock_tag)
const -> LockGuard
588 LockGuard guard{m_mutex, defer_lock_tag};
598 template<
class,
class,
class>
600 template<
class,
class,
class>
602 template<
class,
class,
class>
608 auto Cache<K, V, I, E, C, SV, SK, KH, TS>::lock_pair(CacheType& other)
const -> std::pair<LockGuard, LockGuard>
610 LockGuard my_guard = lock(std::defer_lock);
611 LockGuard other_guard = other.lock(std::defer_lock);
614 std::lock(my_guard, other_guard);
617 return std::make_pair<LockGuard, LockGuard>(std::move(my_guard), std::move(other_guard));
622 template<
class,
class,
class>
624 template<
class,
class,
class>
626 template<
class,
class,
class>
633 void Cache<K, V, I, E, C, SV, SK, KH, TS>::import(Coll& collection)
635 LockGuard guard(lock());
637 for (
auto& [key, value] : collection) {
638 const auto key_size =
static_cast<size_t>(m_measure_key(key));
639 const auto value_size =
static_cast<size_t>(m_measure_value(value));
640 CacheItem item{key_size, std::move(value), value_size};
642 if (!m_constraint_policy->can_add(key, item)) {
646 insert_or_update(std::move(key), std::move(item));
652 template<
class,
class,
class>
654 template<
class,
class,
class>
656 template<
class,
class,
class>
662 bool Cache<K, V, I, E, C, SV, SK, KH, TS>::check_insert(
const K& key,
const CacheItem& item)
664 if (m_constraint_policy->can_add(key, item)) {
665 return m_insertion_policy->should_add(key);
672 auto constraint_copy = std::make_unique<MyConstraintPolicy>(*m_constraint_policy);
673 std::vector<DataMapIt> keys_to_evict;
675 auto should_evict_another = [&](
auto victim_it) {
return victim_it != m_eviction_policy->victim_end() && !constraint_copy->can_add(key, item); };
678 for (
auto victim_it = m_eviction_policy->victim_begin(); should_evict_another(victim_it); ++victim_it) {
679 const K& key_to_evict = *victim_it;
680 auto key_and_item = m_data.find(key_to_evict);
682 if (key_and_item != m_data.end()) {
683 if (!m_insertion_policy->should_replace(key_to_evict, key)) {
689 constraint_copy->on_evict(key_and_item->first, key_and_item->second);
690 keys_to_evict.push_back(key_and_item);
698 if (constraint_copy->can_add(key, item)) {
700 for (
auto key_and_item : keys_to_evict) {
701 remove(key_and_item);
711 template<
class,
class,
class>
713 template<
class,
class,
class>
715 template<
class,
class,
class>
721 bool Cache<K, V, I, E, C, SV, SK, KH, TS>::check_replace(
const K& key,
const CacheItem& old_item,
const CacheItem& new_item)
723 if (m_constraint_policy->can_replace(key, old_item, new_item)) {
732 auto constraint_copy = std::make_unique<MyConstraintPolicy>(*m_constraint_policy);
733 bool evicted_original_key =
false;
735 auto can_replace = [&]() {
736 if (evicted_original_key) {
737 return constraint_copy->can_add(key, new_item);
739 return constraint_copy->can_replace(key, old_item, new_item);
743 auto should_evict_another = [&](
auto victim_it) {
return victim_it != m_eviction_policy->victim_end() && !can_replace(); };
746 std::vector<DataMapIt> keys_to_evict;
748 for (
auto victim_it = m_eviction_policy->victim_begin(); should_evict_another(victim_it); ++victim_it) {
749 const K& key_to_evict = *victim_it;
750 auto key_and_item = m_data.find(key_to_evict);
752 if (key_and_item != m_data.end()) {
753 if (!m_insertion_policy->should_replace(key_to_evict, key)) {
757 if (!evicted_original_key) {
758 evicted_original_key = key_and_item->first == key;
761 constraint_copy->on_evict(key_and_item->first, key_and_item->second);
762 keys_to_evict.push_back(key_and_item);
771 for (
auto key_and_item : keys_to_evict) {
772 remove(key_and_item);
782 template<
class,
class,
class>
784 template<
class,
class,
class>
786 template<
class,
class,
class>
792 void Cache<K, V, I, E, C, SV, SK, KH, TS>::insert_or_update(K&& key, CacheItem&& item)
794 auto key_and_item = m_data.find(key);
795 if (key_and_item != m_data.end()) {
797 swap(key_and_item->second, item);
798 on_update(key_and_item->first, item, key_and_item->second);
801 const auto it_and_ok = m_data.insert_or_assign(std::move(key), std::move(item));
802 assert(it_and_ok.second);
803 on_insert(it_and_ok.first->first, it_and_ok.first->second);
809 template<
class,
class,
class>
811 template<
class,
class,
class>
813 template<
class,
class,
class>
821 on_evict(it->first, it->second);
827 template<
class,
class,
class>
829 template<
class,
class,
class>
831 template<
class,
class,
class>
837 void Cache<K, V, I, E, C, SV, SK, KH, TS>::on_insert(
const K& key,
const CacheItem& item)
const
841 detail::traits::event::has_on_insert<K, KH, V, I>,
842 [&](
auto& x) {
return x.on_insert(key, item); },
843 [](
auto&) {})(*m_insertion_policy);
846 detail::traits::event::has_on_insert<K, KH, V, E>,
847 [&](
auto& x) {
return x.on_insert(key, item); },
848 [](
auto&) {})(*m_eviction_policy);
851 detail::traits::event::has_on_insert<K, KH, V, C>,
852 [&](
auto& x) {
return x.on_insert(key, item); },
853 [](
auto&) {})(*m_constraint_policy);
858 template<
class,
class,
class>
860 template<
class,
class,
class>
862 template<
class,
class,
class>
868 void Cache<K, V, I, E, C, SV, SK, KH, TS>::on_update(
const K& key,
const CacheItem& old_item,
const CacheItem& new_item)
const
872 detail::traits::event::has_on_update<K, KH, V, I>,
873 [&](
auto& x) {
return x.on_update(key, old_item, new_item); },
874 [](
auto&) {})(*m_insertion_policy);
877 detail::traits::event::has_on_update<K, KH, V, E>,
878 [&](
auto& x) {
return x.on_update(key, old_item, new_item); },
879 [](
auto&) {})(*m_eviction_policy);
882 detail::traits::event::has_on_update<K, KH, V, C>,
883 [&](
auto& x) {
return x.on_update(key, old_item, new_item); },
884 [](
auto&) {})(*m_constraint_policy);
889 template<
class,
class,
class>
891 template<
class,
class,
class>
893 template<
class,
class,
class>
899 void Cache<K, V, I, E, C, SV, SK, KH, TS>::on_cache_hit(
const K& key,
const CacheItem& item)
const
903 m_byte_hit_rate_acc(
static_cast<uint32_t
>(item.m_value_size));
907 detail::traits::event::has_on_cachehit<K, KH, V, I>,
908 [&](
auto& x) {
return x.on_cache_hit(key, item); },
909 [](
auto&) {})(*m_insertion_policy);
912 detail::traits::event::has_on_cachehit<K, KH, V, E>,
913 [&](
auto& x) {
return x.on_cache_hit(key, item); },
914 [](
auto&) {})(*m_eviction_policy);
917 detail::traits::event::has_on_cachehit<K, KH, V, E>,
918 [&](
auto& x) {
return x.on_cache_hit(key, item); },
919 [](
auto&) {})(*m_eviction_policy);
924 template<
class,
class,
class>
926 template<
class,
class,
class>
928 template<
class,
class,
class>
934 template<
class KeyView>
935 void Cache<K, V, I, E, C, SV, SK, KH, TS>::on_cache_miss(
const KeyView& key)
const
939 m_byte_hit_rate_acc(0);
943 detail::traits::event::has_on_cachemiss<K, KH, V, I>,
944 [&](
auto& x) {
return x.on_cache_miss(key); },
945 [](
auto&) {})(*m_insertion_policy);
948 detail::traits::event::has_on_cachemiss<K, KH, V, E>,
949 [&](
auto& x) {
return x.on_cache_miss(key); },
950 [](
auto&) {})(*m_eviction_policy);
953 detail::traits::event::has_on_cachemiss<K, KH, V, C>,
954 [&](
auto& x) {
return x.on_cache_miss(key); },
955 [](
auto&) {})(*m_constraint_policy);
960 template<
class,
class,
class>
962 template<
class,
class,
class>
964 template<
class,
class,
class>
970 void Cache<K, V, I, E, C, SV, SK, KH, TS>::on_evict(
const K& key,
const CacheItem& item)
const
974 detail::traits::event::has_on_evict<K, KH, V, I>,
975 [&](
auto& x) {
return x.on_evict(key, item); },
976 [](
auto&) {})(*m_insertion_policy);
979 detail::traits::event::has_on_evict<K, KH, V, E>,
980 [&](
auto& x) {
return x.on_evict(key, item); },
981 [](
auto&) {})(*m_eviction_policy);
984 detail::traits::event::has_on_evict<K, KH, V, C>,
985 [&](
auto& x) {
return x.on_evict(key, item); },
986 [](
auto&) {})(*m_constraint_policy);
991 template<
class,
class,
class>
993 template<
class,
class,
class>
995 template<
class,
class,
class>
1001 void swap(Cache<K, V, I, E, C, SV, SK, KH, TS>& lhs, Cache<K, V, I, E, C, SV, SK, KH, TS>& rhs) noexcept