Skip to content

Commit 8bee6b8

Browse files
committed
Auto merge of #208 - Marwes:probe, r=Amanieu
Remove the need for unwrap when using ProbeSeq Changes the order of operation here a bit so that the code only moves to the next position if it is actually necessary which could be faster, or at least makes for less work for LLVM.
2 parents adf06c2 + cbcd654 commit 8bee6b8

File tree

2 files changed

+50
-41
lines changed

2 files changed

+50
-41
lines changed

benches/bench.rs

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ use test::{black_box, Bencher};
1010

1111
use hashbrown::hash_map::DefaultHashBuilder;
1212
use hashbrown::HashMap;
13-
use std::collections::hash_map::RandomState;
13+
use std::{
14+
collections::hash_map::RandomState,
15+
sync::atomic::{self, AtomicUsize},
16+
};
1417

1518
const SIZE: usize = 1000;
1619

@@ -40,6 +43,20 @@ impl Iterator for RandomKeys {
4043
}
4144
}
4245

46+
// Just an arbitrary side effect to make the maps not shortcircuit to the non-dropping path
47+
// when dropping maps/entries (most real world usages likely have drop in the key or value)
48+
lazy_static::lazy_static! {
49+
static ref SIDE_EFFECT: AtomicUsize = AtomicUsize::new(0);
50+
}
51+
52+
#[derive(Clone)]
53+
struct DropType(usize);
54+
impl Drop for DropType {
55+
fn drop(&mut self) {
56+
SIDE_EFFECT.fetch_add(self.0, atomic::Ordering::SeqCst);
57+
}
58+
}
59+
4360
macro_rules! bench_suite {
4461
($bench_macro:ident, $bench_ahash_serial:ident, $bench_std_serial:ident,
4562
$bench_ahash_highbits:ident, $bench_std_highbits:ident,
@@ -69,10 +86,11 @@ macro_rules! bench_insert {
6986
b.iter(|| {
7087
m.clear();
7188
for i in ($keydist).take(SIZE) {
72-
m.insert(i, i);
89+
m.insert(i, DropType(i));
7390
}
7491
black_box(&mut m);
75-
})
92+
});
93+
eprintln!("{}", SIDE_EFFECT.load(atomic::Ordering::SeqCst));
7694
}
7795
};
7896
}
@@ -93,7 +111,7 @@ macro_rules! bench_insert_erase {
93111
fn $name(b: &mut Bencher) {
94112
let mut base = $maptype::default();
95113
for i in ($keydist).take(SIZE) {
96-
base.insert(i, i);
114+
base.insert(i, DropType(i));
97115
}
98116
let skip = $keydist.skip(SIZE);
99117
b.iter(|| {
@@ -103,11 +121,12 @@ macro_rules! bench_insert_erase {
103121
// While keeping the size constant,
104122
// replace the first keydist with the second.
105123
for (add, remove) in (&mut add_iter).zip(&mut remove_iter).take(SIZE) {
106-
m.insert(add, add);
124+
m.insert(add, DropType(add));
107125
black_box(m.remove(&remove));
108126
}
109127
black_box(m);
110-
})
128+
});
129+
eprintln!("{}", SIDE_EFFECT.load(atomic::Ordering::SeqCst));
111130
}
112131
};
113132
}
@@ -128,14 +147,15 @@ macro_rules! bench_lookup {
128147
fn $name(b: &mut Bencher) {
129148
let mut m = $maptype::default();
130149
for i in $keydist.take(SIZE) {
131-
m.insert(i, i);
150+
m.insert(i, DropType(i));
132151
}
133152

134153
b.iter(|| {
135154
for i in $keydist.take(SIZE) {
136155
black_box(m.get(&i));
137156
}
138-
})
157+
});
158+
eprintln!("{}", SIDE_EFFECT.load(atomic::Ordering::SeqCst));
139159
}
140160
};
141161
}
@@ -157,7 +177,7 @@ macro_rules! bench_lookup_fail {
157177
let mut m = $maptype::default();
158178
let mut iter = $keydist;
159179
for i in (&mut iter).take(SIZE) {
160-
m.insert(i, i);
180+
m.insert(i, DropType(i));
161181
}
162182

163183
b.iter(|| {
@@ -185,7 +205,7 @@ macro_rules! bench_iter {
185205
fn $name(b: &mut Bencher) {
186206
let mut m = $maptype::default();
187207
for i in ($keydist).take(SIZE) {
188-
m.insert(i, i);
208+
m.insert(i, DropType(i));
189209
}
190210

191211
b.iter(|| {
@@ -211,7 +231,7 @@ bench_suite!(
211231
fn clone_small(b: &mut Bencher) {
212232
let mut m = HashMap::new();
213233
for i in 0..10 {
214-
m.insert(i, i);
234+
m.insert(i, DropType(i));
215235
}
216236

217237
b.iter(|| {
@@ -224,7 +244,7 @@ fn clone_from_small(b: &mut Bencher) {
224244
let mut m = HashMap::new();
225245
let mut m2 = HashMap::new();
226246
for i in 0..10 {
227-
m.insert(i, i);
247+
m.insert(i, DropType(i));
228248
}
229249

230250
b.iter(|| {
@@ -237,7 +257,7 @@ fn clone_from_small(b: &mut Bencher) {
237257
fn clone_large(b: &mut Bencher) {
238258
let mut m = HashMap::new();
239259
for i in 0..1000 {
240-
m.insert(i, i);
260+
m.insert(i, DropType(i));
241261
}
242262

243263
b.iter(|| {
@@ -250,7 +270,7 @@ fn clone_from_large(b: &mut Bencher) {
250270
let mut m = HashMap::new();
251271
let mut m2 = HashMap::new();
252272
for i in 0..1000 {
253-
m.insert(i, i);
273+
m.insert(i, DropType(i));
254274
}
255275

256276
b.iter(|| {

src/raw/mod.rs

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -145,27 +145,22 @@ fn h2(hash: u64) -> u8 {
145145
/// Proof that the probe will visit every group in the table:
146146
/// <https://fgiesen.wordpress.com/2015/02/22/triangular-numbers-mod-2n/>
147147
struct ProbeSeq {
148-
bucket_mask: usize,
149148
pos: usize,
150149
stride: usize,
151150
}
152151

153-
impl Iterator for ProbeSeq {
154-
type Item = usize;
155-
152+
impl ProbeSeq {
156153
#[inline]
157-
fn next(&mut self) -> Option<usize> {
154+
fn move_next(&mut self, bucket_mask: usize) {
158155
// We should have found an empty bucket by now and ended the probe.
159156
debug_assert!(
160-
self.stride <= self.bucket_mask,
157+
self.stride <= bucket_mask,
161158
"Went past end of probe sequence"
162159
);
163160

164-
let result = self.pos;
165161
self.stride += Group::WIDTH;
166162
self.pos += self.stride;
167-
self.pos &= self.bucket_mask;
168-
Some(result)
163+
self.pos &= bucket_mask;
169164
}
170165
}
171166

@@ -578,15 +573,14 @@ impl<T> RawTable<T> {
578573
}
579574
}
580575

581-
/// Returns an iterator for a probe sequence on the table.
576+
/// Returns an iterator-like object for a probe sequence on the table.
582577
///
583578
/// This iterator never terminates, but is guaranteed to visit each bucket
584579
/// group exactly once. The loop using `probe_seq` must terminate upon
585580
/// reaching a group containing an empty bucket.
586581
#[cfg_attr(feature = "inline-more", inline)]
587582
fn probe_seq(&self, hash: u64) -> ProbeSeq {
588583
ProbeSeq {
589-
bucket_mask: self.bucket_mask,
590584
pos: h1(hash) & self.bucket_mask,
591585
stride: 0,
592586
}
@@ -626,11 +620,12 @@ impl<T> RawTable<T> {
626620
/// There must be at least 1 empty bucket in the table.
627621
#[cfg_attr(feature = "inline-more", inline)]
628622
fn find_insert_slot(&self, hash: u64) -> usize {
629-
for pos in self.probe_seq(hash) {
623+
let mut probe_seq = self.probe_seq(hash);
624+
loop {
630625
unsafe {
631-
let group = Group::load(self.ctrl(pos));
626+
let group = Group::load(self.ctrl(probe_seq.pos));
632627
if let Some(bit) = group.match_empty_or_deleted().lowest_set_bit() {
633-
let result = (pos + bit) & self.bucket_mask;
628+
let result = (probe_seq.pos + bit) & self.bucket_mask;
634629

635630
// In tables smaller than the group width, trailing control
636631
// bytes outside the range of the table are filled with
@@ -643,7 +638,7 @@ impl<T> RawTable<T> {
643638
// control bytes (containing EMPTY).
644639
if unlikely(is_full(*self.ctrl(result))) {
645640
debug_assert!(self.bucket_mask < Group::WIDTH);
646-
debug_assert_ne!(pos, 0);
641+
debug_assert_ne!(probe_seq.pos, 0);
647642
return Group::load_aligned(self.ctrl(0))
648643
.match_empty_or_deleted()
649644
.lowest_set_bit_nonzero();
@@ -652,10 +647,8 @@ impl<T> RawTable<T> {
652647
}
653648
}
654649
}
650+
probe_seq.move_next(self.bucket_mask);
655651
}
656-
657-
// probe_seq never returns.
658-
unreachable!();
659652
}
660653

661654
/// Marks all table buckets as empty without dropping their contents.
@@ -1872,8 +1865,6 @@ pub struct RawIterHash<'a, T> {
18721865
// The sequence of groups to probe in the search.
18731866
probe_seq: ProbeSeq,
18741867

1875-
// The current group and its position.
1876-
pos: usize,
18771868
group: Group,
18781869

18791870
// The elements within the group with a matching h2-hash.
@@ -1884,16 +1875,14 @@ impl<'a, T> RawIterHash<'a, T> {
18841875
fn new(table: &'a RawTable<T>, hash: u64) -> Self {
18851876
unsafe {
18861877
let h2_hash = h2(hash);
1887-
let mut probe_seq = table.probe_seq(hash);
1888-
let pos = probe_seq.next().unwrap();
1889-
let group = Group::load(table.ctrl(pos));
1878+
let probe_seq = table.probe_seq(hash);
1879+
let group = Group::load(table.ctrl(probe_seq.pos));
18901880
let bitmask = group.match_byte(h2_hash).into_iter();
18911881

18921882
RawIterHash {
18931883
table,
18941884
h2_hash,
18951885
probe_seq,
1896-
pos,
18971886
group,
18981887
bitmask,
18991888
}
@@ -1908,15 +1897,15 @@ impl<'a, T> Iterator for RawIterHash<'a, T> {
19081897
unsafe {
19091898
loop {
19101899
if let Some(bit) = self.bitmask.next() {
1911-
let index = (self.pos + bit) & self.table.bucket_mask;
1900+
let index = (self.probe_seq.pos + bit) & self.table.bucket_mask;
19121901
let bucket = self.table.bucket(index);
19131902
return Some(bucket);
19141903
}
19151904
if likely(self.group.match_empty().any_bit_set()) {
19161905
return None;
19171906
}
1918-
self.pos = self.probe_seq.next().unwrap();
1919-
self.group = Group::load(self.table.ctrl(self.pos));
1907+
self.probe_seq.move_next(self.table.bucket_mask);
1908+
self.group = Group::load(self.table.ctrl(self.probe_seq.pos));
19201909
self.bitmask = self.group.match_byte(self.h2_hash).into_iter();
19211910
}
19221911
}

0 commit comments

Comments
 (0)