Skip to content

Commit ce89c3d

Browse files
committed
Rollup merge of #48035 - technicalguy:Early-exit-empty-hashmap-38880, r=arthurprs
Early exit for empty HashMap (issue #38880) Addresses issue #38880 by checking if the HashMap is empty before computing the value of the hash. Before (integer keys) ``` running 4 tests test empty_once ... bench: 13 ns/iter (+/- 0) test empty_100 ... bench: 1,367 ns/iter (+/- 35) test exist_once ... bench: 14 ns/iter (+/- 0) test exist_100 ... bench: 1,518 ns/iter (+/- 40) ``` After ``` running 4 tests test empty_once ... bench: 2 ns/iter (+/- 0) test empty_100 ... bench: 221 ns/iter (+/- 0) test exist_once ... bench: 15 ns/iter (+/- 0) test exist_100 ... bench: 1,515 ns/iter (+/- 92) ``` When the HashMap is not empty, the performance remains the same, and when it is empty the performance is significantly improved.
2 parents 6436c44 + e034ddd commit ce89c3d

File tree

1 file changed

+38
-26
lines changed
  • src/libstd/collections/hash

1 file changed

+38
-26
lines changed

src/libstd/collections/hash/map.rs

+38-26
Original file line numberDiff line numberDiff line change
@@ -398,8 +398,9 @@ pub struct HashMap<K, V, S = RandomState> {
398398
}
399399

400400
/// Search for a pre-hashed key.
401+
/// If you don't already know the hash, use search or search_mut instead
401402
#[inline]
402-
fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> InternalEntry<K, V, M>
403+
fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, is_match: F) -> InternalEntry<K, V, M>
403404
where M: Deref<Target = RawTable<K, V>>,
404405
F: FnMut(&K) -> bool
405406
{
@@ -410,6 +411,18 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter
410411
return InternalEntry::TableIsEmpty;
411412
}
412413

414+
search_hashed_nonempty(table, hash, is_match)
415+
}
416+
417+
/// Search for a pre-hashed key when the hash map is known to be non-empty.
418+
#[inline]
419+
fn search_hashed_nonempty<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F)
420+
-> InternalEntry<K, V, M>
421+
where M: Deref<Target = RawTable<K, V>>,
422+
F: FnMut(&K) -> bool
423+
{
424+
// Do not check the capacity as an extra branch could slow the lookup.
425+
413426
let size = table.size();
414427
let mut probe = Bucket::new(table, hash);
415428
let mut displacement = 0;
@@ -543,24 +556,36 @@ impl<K, V, S> HashMap<K, V, S>
543556
}
544557

545558
/// Search for a key, yielding the index if it's found in the hashtable.
546-
/// If you already have the hash for the key lying around, use
547-
/// search_hashed.
559+
/// If you already have the hash for the key lying around, or if you need an
560+
/// InternalEntry, use search_hashed or search_hashed_nonempty.
548561
#[inline]
549-
fn search<'a, Q: ?Sized>(&'a self, q: &Q) -> InternalEntry<K, V, &'a RawTable<K, V>>
562+
fn search<'a, Q: ?Sized>(&'a self, q: &Q)
563+
-> Option<FullBucket<K, V, &'a RawTable<K, V>>>
550564
where K: Borrow<Q>,
551565
Q: Eq + Hash
552566
{
567+
if self.is_empty() {
568+
return None;
569+
}
570+
553571
let hash = self.make_hash(q);
554-
search_hashed(&self.table, hash, |k| q.eq(k.borrow()))
572+
search_hashed_nonempty(&self.table, hash, |k| q.eq(k.borrow()))
573+
.into_occupied_bucket()
555574
}
556575

557576
#[inline]
558-
fn search_mut<'a, Q: ?Sized>(&'a mut self, q: &Q) -> InternalEntry<K, V, &'a mut RawTable<K, V>>
577+
fn search_mut<'a, Q: ?Sized>(&'a mut self, q: &Q)
578+
-> Option<FullBucket<K, V, &'a mut RawTable<K, V>>>
559579
where K: Borrow<Q>,
560580
Q: Eq + Hash
561581
{
582+
if self.is_empty() {
583+
return None;
584+
}
585+
562586
let hash = self.make_hash(q);
563-
search_hashed(&mut self.table, hash, |k| q.eq(k.borrow()))
587+
search_hashed_nonempty(&mut self.table, hash, |k| q.eq(k.borrow()))
588+
.into_occupied_bucket()
564589
}
565590

566591
// The caller should ensure that invariants by Robin Hood Hashing hold
@@ -1118,7 +1143,7 @@ impl<K, V, S> HashMap<K, V, S>
11181143
where K: Borrow<Q>,
11191144
Q: Hash + Eq
11201145
{
1121-
self.search(k).into_occupied_bucket().map(|bucket| bucket.into_refs().1)
1146+
self.search(k).map(|bucket| bucket.into_refs().1)
11221147
}
11231148

11241149
/// Returns true if the map contains a value for the specified key.
@@ -1145,7 +1170,7 @@ impl<K, V, S> HashMap<K, V, S>
11451170
where K: Borrow<Q>,
11461171
Q: Hash + Eq
11471172
{
1148-
self.search(k).into_occupied_bucket().is_some()
1173+
self.search(k).is_some()
11491174
}
11501175

11511176
/// Returns a mutable reference to the value corresponding to the key.
@@ -1174,7 +1199,7 @@ impl<K, V, S> HashMap<K, V, S>
11741199
where K: Borrow<Q>,
11751200
Q: Hash + Eq
11761201
{
1177-
self.search_mut(k).into_occupied_bucket().map(|bucket| bucket.into_mut_refs().1)
1202+
self.search_mut(k).map(|bucket| bucket.into_mut_refs().1)
11781203
}
11791204

11801205
/// Inserts a key-value pair into the map.
@@ -1234,11 +1259,7 @@ impl<K, V, S> HashMap<K, V, S>
12341259
where K: Borrow<Q>,
12351260
Q: Hash + Eq
12361261
{
1237-
if self.table.size() == 0 {
1238-
return None;
1239-
}
1240-
1241-
self.search_mut(k).into_occupied_bucket().map(|bucket| pop_internal(bucket).1)
1262+
self.search_mut(k).map(|bucket| pop_internal(bucket).1)
12421263
}
12431264

12441265
/// Removes a key from the map, returning the stored key and value if the
@@ -1269,12 +1290,7 @@ impl<K, V, S> HashMap<K, V, S>
12691290
where K: Borrow<Q>,
12701291
Q: Hash + Eq
12711292
{
1272-
if self.table.size() == 0 {
1273-
return None;
1274-
}
1275-
12761293
self.search_mut(k)
1277-
.into_occupied_bucket()
12781294
.map(|bucket| {
12791295
let (k, v, _) = pop_internal(bucket);
12801296
(k, v)
@@ -2632,15 +2648,11 @@ impl<K, S, Q: ?Sized> super::Recover<Q> for HashMap<K, (), S>
26322648

26332649
#[inline]
26342650
fn get(&self, key: &Q) -> Option<&K> {
2635-
self.search(key).into_occupied_bucket().map(|bucket| bucket.into_refs().0)
2651+
self.search(key).map(|bucket| bucket.into_refs().0)
26362652
}
26372653

26382654
fn take(&mut self, key: &Q) -> Option<K> {
2639-
if self.table.size() == 0 {
2640-
return None;
2641-
}
2642-
2643-
self.search_mut(key).into_occupied_bucket().map(|bucket| pop_internal(bucket).0)
2655+
self.search_mut(key).map(|bucket| pop_internal(bucket).0)
26442656
}
26452657

26462658
#[inline]

0 commit comments

Comments
 (0)