From 43ee7cd57c1706b949c92d6b29a7034b40dff533 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 5 Jun 2025 12:02:04 -0700 Subject: [PATCH 1/3] Simplify and optimize `SlotIndex::from_index` Break out bucket 0 (containing `idx < 4096`) as an early return, which simplifies the remainder of the function, and allows optimizing the `checked_ilog2` since it can no longer return `None`. This reduces the runtime of `vec_cache::tests::slot_index_exhaustive` (which calls `SlotIndex::from_index` for every `u32`, twice) from ~15.5s to ~13.3s. --- .../rustc_data_structures/src/vec_cache.rs | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_data_structures/src/vec_cache.rs b/compiler/rustc_data_structures/src/vec_cache.rs index 2ff60ab7f36f9..cc6973fb42e9a 100644 --- a/compiler/rustc_data_structures/src/vec_cache.rs +++ b/compiler/rustc_data_structures/src/vec_cache.rs @@ -68,22 +68,13 @@ impl SlotIndex { // slots (see `slot_index_exhaustive` in tests). #[inline] const fn from_index(idx: u32) -> Self { - let mut bucket = match idx.checked_ilog2() { - Some(x) => x as usize, - None => 0, - }; - let entries; - let running_sum; - if bucket <= 11 { - entries = 1 << 12; - running_sum = 0; - bucket = 0; - } else { - entries = 1 << bucket; - running_sum = entries; - bucket = bucket - 11; + if idx < 4096 { + return SlotIndex { bucket_idx: 0, entries: 4096, index_in_bucket: idx as usize }; } - SlotIndex { bucket_idx: bucket, entries, index_in_bucket: idx as usize - running_sum } + // SAFETY: We already ruled out idx 0, so `checked_ilog2` can't return `None`. + let bucket = unsafe { idx.checked_ilog2().unwrap_unchecked() as usize }; + let entries = 1 << bucket; + SlotIndex { bucket_idx: bucket - 11, entries, index_in_bucket: idx as usize - entries } } // SAFETY: Buckets must be managed solely by functions here (i.e., get/put on SlotIndex) and From 9837c3c3f8accf763704cc56e2b13398e86db282 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 5 Jun 2025 12:06:21 -0700 Subject: [PATCH 2/3] Simplify `vec_cache::tests::slot_index_exhaustive` by pulling out 0 case `slot_index_exhaustive` has additional complexity in its loop that only applies for index 0. Pull that case out of the loop. --- .../src/vec_cache/tests.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_data_structures/src/vec_cache/tests.rs b/compiler/rustc_data_structures/src/vec_cache/tests.rs index 3b65c14162e8e..9b60913ec922d 100644 --- a/compiler/rustc_data_structures/src/vec_cache/tests.rs +++ b/compiler/rustc_data_structures/src/vec_cache/tests.rs @@ -75,24 +75,21 @@ fn slot_index_exhaustive() { for idx in 0..=u32::MAX { buckets[SlotIndex::from_index(idx).bucket_idx] += 1; } - let mut prev = None::; - for idx in 0..=u32::MAX { + let slot_idx = SlotIndex::from_index(0); + assert_eq!(slot_idx.index_in_bucket, 0); + assert_eq!(slot_idx.bucket_idx, 0); + let mut prev = slot_idx; + for idx in 1..=u32::MAX { let slot_idx = SlotIndex::from_index(idx); - if let Some(p) = prev { - if p.bucket_idx == slot_idx.bucket_idx { - assert_eq!(p.index_in_bucket + 1, slot_idx.index_in_bucket); - } else { - assert_eq!(slot_idx.index_in_bucket, 0); - } + if prev.bucket_idx == slot_idx.bucket_idx { + assert_eq!(prev.index_in_bucket + 1, slot_idx.index_in_bucket); } else { - assert_eq!(idx, 0); assert_eq!(slot_idx.index_in_bucket, 0); - assert_eq!(slot_idx.bucket_idx, 0); } assert_eq!(buckets[slot_idx.bucket_idx], slot_idx.entries as u32); assert_eq!(ENTRIES_BY_BUCKET[slot_idx.bucket_idx], slot_idx.entries, "{}", idx); - prev = Some(slot_idx); + prev = slot_idx; } } From 8a56ed4c89d38618e065f7d9ec6be0390fdabe31 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 5 Jun 2025 16:57:59 -0700 Subject: [PATCH 3/3] `SlotIndex::from_index`: Factor out a constant for the first bucket size --- compiler/rustc_data_structures/src/vec_cache.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_data_structures/src/vec_cache.rs b/compiler/rustc_data_structures/src/vec_cache.rs index cc6973fb42e9a..3b448c056b748 100644 --- a/compiler/rustc_data_structures/src/vec_cache.rs +++ b/compiler/rustc_data_structures/src/vec_cache.rs @@ -68,13 +68,22 @@ impl SlotIndex { // slots (see `slot_index_exhaustive` in tests). #[inline] const fn from_index(idx: u32) -> Self { - if idx < 4096 { - return SlotIndex { bucket_idx: 0, entries: 4096, index_in_bucket: idx as usize }; + const FIRST_BUCKET_SHIFT: usize = 12; + if idx < (1 << FIRST_BUCKET_SHIFT) { + return SlotIndex { + bucket_idx: 0, + entries: 1 << FIRST_BUCKET_SHIFT, + index_in_bucket: idx as usize, + }; } // SAFETY: We already ruled out idx 0, so `checked_ilog2` can't return `None`. let bucket = unsafe { idx.checked_ilog2().unwrap_unchecked() as usize }; let entries = 1 << bucket; - SlotIndex { bucket_idx: bucket - 11, entries, index_in_bucket: idx as usize - entries } + SlotIndex { + bucket_idx: bucket - FIRST_BUCKET_SHIFT + 1, + entries, + index_in_bucket: idx as usize - entries, + } } // SAFETY: Buckets must be managed solely by functions here (i.e., get/put on SlotIndex) and