Skip to content

Commit 03bdaad

Browse files
authored
Auto merge of #38022 - arthurprs:micro-opt-hm, r=bluss
Use displacement instead of initial bucket in HashMap code Use displacement instead of initial bucket in HashMap code. It makes the code a bit cleaner and also saves a few instructions (handy since it'll be using some to do some sort of adaptive behavior soon).
2 parents 9ca50bd + 178e29d commit 03bdaad

File tree

1 file changed

+21
-18
lines changed
  • src/libstd/collections/hash

1 file changed

+21
-18
lines changed

src/libstd/collections/hash/map.rs

+21-18
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,9 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter
371371
return InternalEntry::TableIsEmpty;
372372
}
373373

374-
let size = table.size() as isize;
374+
let size = table.size();
375375
let mut probe = Bucket::new(table, hash);
376-
let ib = probe.index() as isize;
376+
let mut displacement = 0;
377377

378378
loop {
379379
let full = match probe.peek() {
@@ -387,15 +387,15 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter
387387
Full(bucket) => bucket,
388388
};
389389

390-
let robin_ib = full.index() as isize - full.displacement() as isize;
390+
let probe_displacement = full.displacement();
391391

392-
if ib < robin_ib {
392+
if probe_displacement < displacement {
393393
// Found a luckier bucket than me.
394394
// We can finish the search early if we hit any bucket
395395
// with a lower distance to initial bucket than we've probed.
396396
return InternalEntry::Vacant {
397397
hash: hash,
398-
elem: NeqElem(full, robin_ib as usize),
398+
elem: NeqElem(full, probe_displacement),
399399
};
400400
}
401401

@@ -406,9 +406,9 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter
406406
return InternalEntry::Occupied { elem: full };
407407
}
408408
}
409-
409+
displacement += 1;
410410
probe = full.next();
411-
debug_assert!(probe.index() as isize != ib + size + 1);
411+
debug_assert!(displacement <= size);
412412
}
413413
}
414414

@@ -431,12 +431,11 @@ fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> (K, V) {
431431
}
432432

433433
/// Perform robin hood bucket stealing at the given `bucket`. You must
434-
/// also pass the position of that bucket's initial bucket so we don't have
435-
/// to recalculate it.
434+
/// also pass that bucket's displacement so we don't have to recalculate it.
436435
///
437436
/// `hash`, `k`, and `v` are the elements to "robin hood" into the hashtable.
438437
fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>,
439-
mut ib: usize,
438+
mut displacement: usize,
440439
mut hash: SafeHash,
441440
mut key: K,
442441
mut val: V)
@@ -457,6 +456,7 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>,
457456
val = old_val;
458457

459458
loop {
459+
displacement += 1;
460460
let probe = bucket.next();
461461
debug_assert!(probe.index() != idx_end);
462462

@@ -476,13 +476,13 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>,
476476
Full(bucket) => bucket,
477477
};
478478

479-
let probe_ib = full_bucket.index() - full_bucket.displacement();
479+
let probe_displacement = full_bucket.displacement();
480480

481481
bucket = full_bucket;
482482

483483
// Robin hood! Steal the spot.
484-
if ib < probe_ib {
485-
ib = probe_ib;
484+
if probe_displacement < displacement {
485+
displacement = probe_displacement;
486486
break;
487487
}
488488
}
@@ -520,13 +520,16 @@ impl<K, V, S> HashMap<K, V, S>
520520
search_hashed(&mut self.table, hash, |k| q.eq(k.borrow()))
521521
}
522522

523-
// The caller should ensure that invariants by Robin Hood Hashing hold.
523+
// The caller should ensure that invariants by Robin Hood Hashing hold
524+
// and that there's space in the underlying table.
524525
fn insert_hashed_ordered(&mut self, hash: SafeHash, k: K, v: V) {
525526
let raw_cap = self.raw_capacity();
526527
let mut buckets = Bucket::new(&mut self.table, hash);
527-
let ib = buckets.index();
528+
// note that buckets.index() keeps increasing
529+
// even if the pointer wraps back to the first bucket.
530+
let limit_bucket = buckets.index() + raw_cap;
528531

529-
while buckets.index() != ib + raw_cap {
532+
loop {
530533
// We don't need to compare hashes for value swap.
531534
// Not even DIBs for Robin Hood.
532535
buckets = match buckets.peek() {
@@ -537,8 +540,8 @@ impl<K, V, S> HashMap<K, V, S>
537540
Full(b) => b.into_bucket(),
538541
};
539542
buckets.next();
543+
debug_assert!(buckets.index() < limit_bucket);
540544
}
541-
panic!("Internal HashMap error: Out of space.");
542545
}
543546
}
544547

@@ -1959,7 +1962,7 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> {
19591962
#[stable(feature = "rust1", since = "1.0.0")]
19601963
pub fn insert(self, value: V) -> &'a mut V {
19611964
match self.elem {
1962-
NeqElem(bucket, ib) => robin_hood(bucket, ib, self.hash, self.key, value),
1965+
NeqElem(bucket, disp) => robin_hood(bucket, disp, self.hash, self.key, value),
19631966
NoElem(bucket) => bucket.put(self.hash, self.key, value).into_mut_refs().1,
19641967
}
19651968
}

0 commit comments

Comments
 (0)