Skip to content

Commit 4ec1f23

Browse files
authored
Merge pull request #589 from evoskuil/master
Integration of hashmap sieve.
2 parents fe9d1a2 + 452ef8c commit 4ec1f23

15 files changed

Lines changed: 1269 additions & 1046 deletions

File tree

include/bitcoin/database/impl/primitives/hashhead.ipp

Lines changed: 76 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ TEMPLATE
115115
inline Link CLASS::index(const Key& key) const NOEXCEPT
116116
{
117117
using namespace system;
118-
const auto index = possible_narrow_cast<link>(keys::hash<Key>(key));
118+
const auto index = possible_narrow_cast<link>(keys::hash(key));
119119
return bit_and<link>(mask_, index);
120120
}
121121

@@ -129,37 +129,28 @@ TEMPLATE
129129
inline Link CLASS::top(const Key& key) const NOEXCEPT
130130
{
131131
const auto value = get_cell(index(key));
132-
if (is_collision(value, key))
132+
if (screened(value, key))
133133
return to_link(value);
134-
134+
135+
// Conflict (body) search is bypassed by filter when key is not screened.
136+
// If terminal here it is assured that table does not contain the key.
135137
return {};
136138
}
137139

138140
TEMPLATE
139141
inline bool CLASS::push(const Link& current, bytes& next,
140142
const Key& key) NOEXCEPT
141143
{
142-
return push(current, next, index(key));
143-
}
144-
145-
TEMPLATE
146-
inline bool CLASS::push(const Link& current, bytes& next,
147-
const Link& index) NOEXCEPT
148-
{
149-
return set_cell(next, current, index) != terminal;
144+
bool unused{};
145+
return push(unused, current, next, key);
150146
}
151147

152148
TEMPLATE
153149
inline bool CLASS::push(bool& collision, const Link& current, bytes& next,
154150
const Key& key) NOEXCEPT
155151
{
156-
const auto previous = set_cell(next, current, index(key));
157-
if (previous == terminal)
158-
return false;
159-
160-
// Caller searches Link{ next } for duplicate in case of filter collision.
161-
collision = is_collision(previous, key);
162-
return true;
152+
// next holds previous top and can searched for dups if collision is true.
153+
return set_cell(collision, next, current, key);
163154
}
164155

165156
// protected
@@ -181,31 +172,31 @@ inline CLASS::cell CLASS::get_cell(const Link& index) const NOEXCEPT
181172
////const std::atomic_ref<cell> top(unsafe_byte_cast<cell>(raw));
182173
const auto& top = *pointer_cast<std::atomic<cell>>(raw);
183174

184-
// Acquire is necessary to synchronize with push release.
175+
// Acquire is necessary to synchronize with set_cell release.
185176
// Relaxed would miss next updates, so acquire is optimal.
186177
return top.load(std::memory_order_acquire);
187178
}
188179
else
189180
{
190-
cell top{};
191-
const auto& head = cell_array(raw);
181+
const auto& top = cell_array(raw);
182+
cell head{};
192183

193184
mutex_.lock_shared();
194-
cell_array(top) = head;
185+
cell_array(head) = top;
195186
mutex_.unlock_shared();
196187

197-
return top;
188+
return head;
198189
}
199190
}
200191

201192
TEMPLATE
202-
inline CLASS::cell CLASS::set_cell(bytes& next, const Link& current,
203-
const Link& index) NOEXCEPT
193+
inline bool CLASS::set_cell(bool& collision, bytes& next, const Link& current,
194+
const Key& key) NOEXCEPT
204195
{
205196
using namespace system;
206-
const auto raw = file_.get_raw(link_to_position(index));
197+
const auto raw = file_.get_raw(link_to_position(index(key)));
207198
if (is_null(raw))
208-
return terminal;
199+
return false;
209200

210201
if constexpr (aligned)
211202
{
@@ -214,28 +205,30 @@ inline CLASS::cell CLASS::set_cell(bytes& next, const Link& current,
214205
////const std::atomic_ref<cell> head(unsafe_byte_cast<cell>(raw));
215206
auto& top = *pointer_cast<std::atomic<cell>>(raw);
216207
auto head = top.load(std::memory_order_acquire);
208+
cell update{};
217209
do
218210
{
219211
// Compiler could order this after top.store, which would expose key
220212
// to search before next entry is linked. Thread fence imposes order.
221213
// A release fence ensures that all prior writes (like next) are
222214
// completed before any subsequent atomic store.
223215
next = link_array(head);
216+
update = to_cell(collision, head, current, key);
224217
std::atomic_thread_fence(std::memory_order_release);
225218
}
226-
while (!top.compare_exchange_weak(head, to_cell(head, current),
219+
while (!top.compare_exchange_weak(head, update,
227220
std::memory_order_release, std::memory_order_acquire));
228221
}
229222
else
230223
{
231-
cell top{};
232-
auto& head = cell_array(raw);
224+
auto& top = cell_array(raw);
225+
cell head{};
233226

234227
mutex_.lock();
235-
cell_array(top) = head;
236-
next = link_array(top);
237-
auto bytes = to_cell(top, current);
238-
head = cell_array(bytes);
228+
cell_array(head) = top;
229+
next = link_array(head);
230+
auto update = to_cell(collision, head, current, key);
231+
top = cell_array(update);
239232
mutex_.unlock();
240233
}
241234

@@ -247,31 +240,67 @@ inline CLASS::cell CLASS::set_cell(bytes& next, const Link& current,
247240
// filters
248241

249242
TEMPLATE
250-
constexpr CLASS::link CLASS::to_link(cell value) NOEXCEPT
243+
INLINE constexpr bool CLASS::screened(cell value, const Key& key) NOEXCEPT
251244
{
252-
if (value == terminal)
253-
return {};
245+
if constexpr (sieve_t::disabled)
246+
{
247+
return true;
248+
}
249+
else
250+
{
251+
return sieve_t::is_screened(to_filter(value), fingerprint(key));
252+
}
253+
}
254254

255+
TEMPLATE
256+
INLINE constexpr CLASS::filter CLASS::fingerprint(const Key& key) NOEXCEPT
257+
{
255258
using namespace system;
256-
constexpr auto mask = unmask_right<cell>(link_bits);
257-
return possible_narrow_cast<link>(bit_and(value, mask));
259+
return possible_narrow_cast<filter>(keys::thumb(key));
258260
}
259261

260262
TEMPLATE
261-
constexpr CLASS::cell CLASS::to_cell(cell, link current) NOEXCEPT
263+
INLINE constexpr CLASS::filter CLASS::to_filter(cell value) NOEXCEPT
262264
{
263-
// TODO:
264-
return current;
265+
using namespace system;
266+
return possible_narrow_cast<filter>(shift_right(value, link_bits));
265267
}
266268

267269
TEMPLATE
268-
constexpr bool CLASS::is_collision(cell value, const Key&) NOEXCEPT
270+
INLINE constexpr CLASS::link CLASS::to_link(cell value) NOEXCEPT
269271
{
270-
if (value == terminal)
271-
return false;
272+
if constexpr (sieve_t::disabled)
273+
{
274+
return system::possible_narrow_cast<link>(value);
275+
}
276+
else
277+
{
278+
using namespace system;
279+
if (value == terminal)
280+
return {};
272281

273-
// TODO:
274-
return true;
282+
constexpr auto mask = unmask_right<cell>(link_bits);
283+
return possible_narrow_cast<link>(bit_and(value, mask));
284+
}
285+
}
286+
287+
TEMPLATE
288+
INLINE constexpr CLASS::cell CLASS::to_cell(bool& collision, cell previous,
289+
link current, const Key& key) NOEXCEPT
290+
{
291+
if constexpr (sieve_t::disabled)
292+
{
293+
collision = true;
294+
return current;
295+
}
296+
else
297+
{
298+
using namespace system;
299+
const auto sieve = to_filter(previous);
300+
const auto next = sieve_t::screen(sieve, fingerprint(key));
301+
collision = (next == sieve || sieve_t::is_saturated(next));
302+
return bit_or<cell>(shift_left<cell>(next, link_bits), current);
303+
}
275304
}
276305

277306
} // namespace database

include/bitcoin/database/impl/primitives/hashmap.ipp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,16 @@ ELEMENT_CONSTRAINT
373373
inline bool CLASS::put(bool& duplicate, const memory_ptr& ptr,
374374
const Link& link, const Key& key, const Element& element) NOEXCEPT
375375
{
376-
Link previous_head{};
377-
if (!write(previous_head, ptr, link, key, element))
376+
Link previous{};
377+
if (!write(previous, ptr, link, key, element))
378378
return false;
379379

380-
duplicate = !first(ptr, previous_head, key).is_terminal();
380+
if (previous.is_terminal())
381+
ncounter_.fetch_add(one, std::memory_order_relaxed);
382+
else
383+
pcounter_.fetch_add(one, std::memory_order_relaxed);
384+
385+
duplicate = !first(ptr, previous, key).is_terminal();
381386
return true;
382387
}
383388

@@ -411,7 +416,7 @@ bool CLASS::commit(const memory_ptr& ptr, const Link& link,
411416

412417
// Commit element to search index (terminal is a valid bucket index).
413418
auto& next = unsafe_array_cast<uint8_t, Link::size>(offset);
414-
return head_.push(link, next, head_.index(key));
419+
return head_.push(link, next, key);
415420
}
416421

417422
// protected
@@ -509,7 +514,7 @@ bool CLASS::write(const memory_ptr& ptr, const Link& link, const Key& key,
509514
// Commit element to body and search (terminal is a valid bucket index).
510515
if constexpr (!is_slab) { BC_DEBUG_ONLY(sink.set_limit(RowSize * element.count());) }
511516
auto& next = unsafe_array_cast<uint8_t, Link::size>(offset);
512-
return element.to_data(sink) && head_.push(link, next, head_.index(key));
517+
return element.to_data(sink) && head_.push(link, next, key);
513518
}
514519

515520
TEMPLATE
@@ -548,11 +553,15 @@ bool CLASS::write(Link& previous, const memory_ptr& ptr, const Link& link,
548553

549554
// Commit element to search (terminal is a valid bucket index).
550555
bool collision{};
551-
if (!head_.push(collision, link, next, head_.index(key)))
556+
if (!head_.push(collision, link, next, key))
552557
return false;
553558

554559
// If filter collision set previous stack head for conflict resolution.
555-
previous = collision ? next : Link::terminal;
560+
if (collision)
561+
previous = next;
562+
else
563+
previous = Link::terminal;
564+
556565
return true;
557566
}
558567

0 commit comments

Comments
 (0)