Skip to content

Commit ac634bc

Browse files
authored
Rollup merge of #97215 - AngelicosPhosphoros:add_hashtable_iteration_complexity_note, r=thomcc
Add complexity estimation of iterating over HashSet and HashMap It is not obvious (at least for me) that complexity of iteration over hash tables depends on capacity and not length. Especially comparing with other containers like Vec or String. I think, this behaviour is worth mentioning. I run benchmark which tests iteration time for maps with length 50 and different capacities and get this results: ``` capacity - time 64 - 203.87 ns 256 - 351.78 ns 1024 - 607.87 ns 4096 - 965.82 ns 16384 - 3.1188 us ``` If you want to dig why it behaves such way, you can look current implementation in [hashbrown code](https://github.com/rust-lang/hashbrown/blob/f3a9f211d06f78c5beb81ac22ea08fdc269e068f/src/raw/mod.rs#L1933). Benchmarks code would be presented in PR related to this commit.
2 parents 2941434 + de97d73 commit ac634bc

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

library/std/src/collections/hash/map.rs

+40
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,11 @@ impl<K, V, S> HashMap<K, V, S> {
344344
/// println!("{key}");
345345
/// }
346346
/// ```
347+
///
348+
/// # Performance
349+
///
350+
/// In the current implementation, iterating over keys takes O(capacity) time
351+
/// instead of O(len) because it internally visits empty buckets too.
347352
#[stable(feature = "rust1", since = "1.0.0")]
348353
pub fn keys(&self) -> Keys<'_, K, V> {
349354
Keys { inner: self.iter() }
@@ -370,6 +375,11 @@ impl<K, V, S> HashMap<K, V, S> {
370375
/// vec.sort_unstable();
371376
/// assert_eq!(vec, ["a", "b", "c"]);
372377
/// ```
378+
///
379+
/// # Performance
380+
///
381+
/// In the current implementation, iterating over keys takes O(capacity) time
382+
/// instead of O(len) because it internally visits empty buckets too.
373383
#[inline]
374384
#[rustc_lint_query_instability]
375385
#[stable(feature = "map_into_keys_values", since = "1.54.0")]
@@ -395,6 +405,11 @@ impl<K, V, S> HashMap<K, V, S> {
395405
/// println!("{val}");
396406
/// }
397407
/// ```
408+
///
409+
/// # Performance
410+
///
411+
/// In the current implementation, iterating over values takes O(capacity) time
412+
/// instead of O(len) because it internally visits empty buckets too.
398413
#[stable(feature = "rust1", since = "1.0.0")]
399414
pub fn values(&self) -> Values<'_, K, V> {
400415
Values { inner: self.iter() }
@@ -422,6 +437,11 @@ impl<K, V, S> HashMap<K, V, S> {
422437
/// println!("{val}");
423438
/// }
424439
/// ```
440+
///
441+
/// # Performance
442+
///
443+
/// In the current implementation, iterating over values takes O(capacity) time
444+
/// instead of O(len) because it internally visits empty buckets too.
425445
#[stable(feature = "map_values_mut", since = "1.10.0")]
426446
pub fn values_mut(&mut self) -> ValuesMut<'_, K, V> {
427447
ValuesMut { inner: self.iter_mut() }
@@ -448,6 +468,11 @@ impl<K, V, S> HashMap<K, V, S> {
448468
/// vec.sort_unstable();
449469
/// assert_eq!(vec, [1, 2, 3]);
450470
/// ```
471+
///
472+
/// # Performance
473+
///
474+
/// In the current implementation, iterating over values takes O(capacity) time
475+
/// instead of O(len) because it internally visits empty buckets too.
451476
#[inline]
452477
#[rustc_lint_query_instability]
453478
#[stable(feature = "map_into_keys_values", since = "1.54.0")]
@@ -473,6 +498,11 @@ impl<K, V, S> HashMap<K, V, S> {
473498
/// println!("key: {key} val: {val}");
474499
/// }
475500
/// ```
501+
///
502+
/// # Performance
503+
///
504+
/// In the current implementation, iterating over map takes O(capacity) time
505+
/// instead of O(len) because it internally visits empty buckets too.
476506
#[rustc_lint_query_instability]
477507
#[stable(feature = "rust1", since = "1.0.0")]
478508
pub fn iter(&self) -> Iter<'_, K, V> {
@@ -503,6 +533,11 @@ impl<K, V, S> HashMap<K, V, S> {
503533
/// println!("key: {key} val: {val}");
504534
/// }
505535
/// ```
536+
///
537+
/// # Performance
538+
///
539+
/// In the current implementation, iterating over map takes O(capacity) time
540+
/// instead of O(len) because it internally visits empty buckets too.
506541
#[rustc_lint_query_instability]
507542
#[stable(feature = "rust1", since = "1.0.0")]
508543
pub fn iter_mut(&mut self) -> IterMut<'_, K, V> {
@@ -633,6 +668,11 @@ impl<K, V, S> HashMap<K, V, S> {
633668
/// map.retain(|&k, _| k % 2 == 0);
634669
/// assert_eq!(map.len(), 4);
635670
/// ```
671+
///
672+
/// # Performance
673+
///
674+
/// In the current implementation, this operation takes O(capacity) time
675+
/// instead of O(len) because it internally visits empty buckets too.
636676
#[inline]
637677
#[rustc_lint_query_instability]
638678
#[stable(feature = "retain_hash_collection", since = "1.18.0")]

library/std/src/collections/hash/set.rs

+10
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ impl<T, S> HashSet<T, S> {
184184
/// println!("{x}");
185185
/// }
186186
/// ```
187+
///
188+
/// # Performance
189+
///
190+
/// In the current implementation, iterating over set takes O(capacity) time
191+
/// instead of O(len) because it internally visits empty buckets too.
187192
#[inline]
188193
#[rustc_lint_query_instability]
189194
#[stable(feature = "rust1", since = "1.0.0")]
@@ -312,6 +317,11 @@ impl<T, S> HashSet<T, S> {
312317
/// set.retain(|&k| k % 2 == 0);
313318
/// assert_eq!(set.len(), 3);
314319
/// ```
320+
///
321+
/// # Performance
322+
///
323+
/// In the current implementation, this operation takes O(capacity) time
324+
/// instead of O(len) because it internally visits empty buckets too.
315325
#[rustc_lint_query_instability]
316326
#[stable(feature = "retain_hash_collection", since = "1.18.0")]
317327
pub fn retain<F>(&mut self, f: F)

0 commit comments

Comments
 (0)