Ultra 0.9.4
Loading...
Searching...
No Matches
cache.tcc
Go to the documentation of this file.
1
12
13#if !defined(ULTRA_CACHE_H)
14# error "Don't include this file directly, include the specific .h instead"
15#endif
16
17#if !defined(ULTRA_CACHE_TCC)
18#define ULTRA_CACHE_TCC
19
20namespace internal
21{
22
30[[nodiscard]] inline std::size_t div_ceil(std::size_t x, std::size_t y)
31{
32 // Most common architecture's divide instruction also includes remainder in
33 // its result so this really needs only one division and would be very fast.
34 return x/y + (x%y != 0);
35
36 // `(x + y - 1) / y` is an alternative (but could overflow).
37}
38
39} // namespace internal
40
41template<Fitness F, std::size_t LOCK_GROUP_SIZE>
42std::size_t cache<F, LOCK_GROUP_SIZE>::table_size(bitwidth n)
43{
44 Expects(n <= max_cache_bitwidth);
45 return 1uz << n;
46}
47
56template<Fitness F, std::size_t LOCK_GROUP_SIZE>
57cache<F, LOCK_GROUP_SIZE>::cache(bitwidth n)
58 : table_(table_size(n)),
59 locks_(internal::div_ceil(table_.size(), LOCK_GROUP_SIZE)),
60 k_mask(table_.size() - 1)
61{
62 Ensures(is_valid());
63}
64
74template<Fitness F, std::size_t LOCK_GROUP_SIZE>
75void cache<F, LOCK_GROUP_SIZE>::resize(bitwidth n)
76{
77 const std::size_t nelem(table_size(n));
78
79 table_ = decltype(table_)(nelem);
80 locks_ = decltype(locks_)(internal::div_ceil(nelem, LOCK_GROUP_SIZE));
81 k_mask = nelem - 1;
82
83 Ensures(is_valid());
84}
85
90template<Fitness F, std::size_t LOCK_GROUP_SIZE>
91std::size_t cache<F, LOCK_GROUP_SIZE>::index(const hash_t &h) const noexcept
92{
93 Expects(bits());
94 return h.data[0] & k_mask;
95}
96
102template<Fitness F, std::size_t LOCK_GROUP_SIZE>
103std::size_t
104cache<F, LOCK_GROUP_SIZE>::lock_index(std::size_t idx) const noexcept
105{
106 return idx / LOCK_GROUP_SIZE;
107}
108
118template<Fitness F, std::size_t LOCK_GROUP_SIZE>
119void cache<F, LOCK_GROUP_SIZE>::clear() noexcept
120{
121 if (++seal_ == 0)
122 {
123 for (auto &slot : table_)
124 slot.seal = 0;
125
126 seal_ = 1;
127 }
128
129 Ensures(is_valid());
130}
131
137template<Fitness F, std::size_t LOCK_GROUP_SIZE>
138void cache<F, LOCK_GROUP_SIZE>::clear(const hash_t &h)
139{
140 const auto idx(index(h));
141 auto &mutex(locks_[lock_index(idx)]);
142
143 std::lock_guard lock(mutex);
144
145 // Invalidates the slot since the first valid value for seal is `1`.
146 table_[idx].seal = 0;
147}
148
156template<Fitness F, std::size_t LOCK_GROUP_SIZE>
157std::optional<F> cache<F, LOCK_GROUP_SIZE>::find(const hash_t &h) const
158{
159 const auto idx(index(h));
160 auto &mutex(locks_[lock_index(idx)]);
161
162 {
163 std::shared_lock lock(mutex);
164
165 if (const slot &s(table_[idx]); s.seal == seal_ && s.hash == h)
166 return s.fitness;
167 }
168
169 return std::nullopt;
170}
171
179template<Fitness F, std::size_t LOCK_GROUP_SIZE>
180void cache<F, LOCK_GROUP_SIZE>::insert(const hash_t &h, const F &fitness)
181{
182 const auto idx(index(h));
183 auto &mutex(locks_[lock_index(idx)]);
184
185 std::lock_guard lock(mutex);
186
187 table_[idx] = {h, fitness, seal_};
188}
189
202template<Fitness F, std::size_t LOCK_GROUP_SIZE>
203bool cache<F, LOCK_GROUP_SIZE>::load(std::istream &in)
204{
205 clear();
206
207 std::size_t n;
208 if (!(in >> n))
209 return false;
210
211 while (n)
212 {
213 slot s;
214 s.seal = seal_;
215
216 if (!s.hash.load(in))
217 return false;
218 if (!ultra::load(in, &s.fitness))
219 return false;
220
221 table_[index(s.hash)] = s;
222
223 --n;
224 }
225
226 return true;
227}
228
236template<Fitness F, std::size_t LOCK_GROUP_SIZE>
237bool cache<F, LOCK_GROUP_SIZE>::save(std::ostream &out) const
238{
239 std::size_t num(std::ranges::count_if(
240 table_, [this](const auto &s) {return s.seal == seal_;}));
241
242 out << num << '\n';
243
244 for (const auto &s : table_)
245 if (s.seal == seal_)
246 {
247 if (!s.hash.save(out))
248 return false;
249 if (!ultra::save(out, s.fitness))
250 return false;
251
252 --num;
253 }
254
255 Expects(num == 0);
256 return out.good();
257}
258
262template<Fitness F, std::size_t LOCK_GROUP_SIZE>
263bitwidth cache<F, LOCK_GROUP_SIZE>::bits() const
264{
265 return std::bit_width(k_mask);
266}
267
274template<Fitness F, std::size_t LOCK_GROUP_SIZE>
275bool cache<F, LOCK_GROUP_SIZE>::is_valid() const
276{
277 if (seal_ == 0)
278 return false;
279
280 if (table_.empty())
281 return k_mask == 0;
282
283 return std::has_single_bit(table_.size()) && table_.size() - 1 == k_mask;
284}
285
286#endif // include guard
std::size_t div_ceil(std::size_t x, std::size_t y)
Definition cache.h:31
std::size_t div_ceil(std::size_t x, std::size_t y)
Definition cache.tcc:30
bool save(std::ostream &out, F f)
Definition fitness.h:151
unsigned bitwidth
Type alias for the number of bits.
Definition cache.h:30