Skip to content
This repository was archived by the owner on Jan 5, 2026. It is now read-only.

Commit 6d6c52b

Browse files
authored
fix(hash*, #194): reduce future sizes (#209)
reduce future sizes by ~48B.
1 parent 36082ae commit 6d6c52b

12 files changed

Lines changed: 503 additions & 431 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33

44
## Version 3
55

6+
3.4.3
7+
8+
* Maximum theoretically capacity is adjusted to `2^(usize::BITS - 2)`.
9+
* Minor `Future` size improvement.
10+
611
3.4.2
712

813
* Minor optimization.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "scc"
33
description = "A collection of high-performance containers providing both asynchronous and synchronous interfaces"
44
documentation = "https://docs.rs/scc"
5-
version = "3.4.2"
5+
version = "3.4.3"
66
authors = ["wvwwvwwv <wvwwvwwv@me.com>"]
77
edition = "2024"
88
rust-version = "1.85.0"
@@ -26,7 +26,7 @@ serde = { version = "1.0", optional = true }
2626
loom = ["dep:loom", "saa/loom", "sdd/loom"]
2727

2828
[dev-dependencies]
29-
criterion = { version = "0.7", features = ["async_futures"] }
29+
criterion = { version = "0.8", features = ["async_futures"] }
3030
fnv = "1.0"
3131
futures = "0.3"
3232
proptest = "1.9"

src/async_helper.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ pub(crate) trait TryWait {
3030
fn try_wait(&mut self, lock: &Lock);
3131
}
3232

33+
/// Returns a fake [`Guard`] reference for methods that require a [`Guard`] to check the lifetime.
34+
#[inline]
35+
pub(super) const fn fake_guard() -> &'static Guard {
36+
unsafe { &*ptr::from_ref(&FAKE_GUARD_GLOBAL).cast::<Guard>() }
37+
}
38+
3339
impl AsyncGuard {
3440
/// Returns `true` if the [`AsyncGuard`] contains a valid [`Guard`].
3541
#[inline]
@@ -56,18 +62,21 @@ impl AsyncGuard {
5662
}
5763
}
5864

59-
/// Loads the content of the [`AtomicShared`] without exposing the [`Guard`].
65+
/// Loads the content of the [`AtomicShared`] without exposing the [`Guard`] or checking tag
66+
/// bits.
6067
#[inline]
61-
pub(crate) fn load<T>(&self, atomic_ptr: &AtomicShared<T>, mo: Ordering) -> Option<&T> {
62-
atomic_ptr.load(mo, self.guard()).as_ref()
68+
pub(crate) fn load_unchecked<T>(
69+
&self,
70+
atomic_ptr: &AtomicShared<T>,
71+
mo: Ordering,
72+
) -> Option<&T> {
73+
unsafe { atomic_ptr.load(mo, self.guard()).as_ref_unchecked() }
6374
}
6475

6576
/// Checks if the reference is valid.
6677
#[inline]
6778
pub(crate) fn check_ref<T>(&self, atomic_ptr: &AtomicShared<T>, r: &T, mo: Ordering) -> bool {
68-
atomic_ptr
69-
.load(mo, self.guard())
70-
.as_ref()
79+
self.load_unchecked(atomic_ptr, mo)
7180
.is_some_and(|s| ptr::eq(s, r))
7281
}
7382
}
@@ -120,3 +129,5 @@ impl TryWait for () {
120129
let _: Result<_, _> = pinned_pager.poll_sync();
121130
}
122131
}
132+
133+
static FAKE_GUARD_GLOBAL: usize = 0;

src/hash_cache.rs

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,20 @@ use std::fmt::{self, Debug};
55
use std::hash::{BuildHasher, Hash};
66
use std::mem::replace;
77
use std::ops::{Deref, DerefMut, RangeInclusive};
8-
use std::pin::pin;
8+
#[cfg(not(feature = "loom"))]
99
use std::sync::atomic::AtomicUsize;
1010
use std::sync::atomic::Ordering::Relaxed;
1111

12+
#[cfg(feature = "loom")]
13+
use loom::sync::atomic::AtomicUsize;
1214
use sdd::{AtomicShared, Guard, Shared, Tag};
1315

1416
use super::Equivalent;
17+
use super::async_helper::fake_guard;
18+
use super::hash_table::MAXIMUM_CAPACITY_LIMIT;
1519
use super::hash_table::bucket::{CACHE, DoublyLinkedList, EntryPtr};
1620
use super::hash_table::bucket_array::BucketArray;
1721
use super::hash_table::{HashTable, LockedBucket};
18-
use crate::async_helper::AsyncGuard;
1922

2023
/// Scalable concurrent 32-way associative cache backed by [`HashMap`](super::HashMap).
2124
///
@@ -189,7 +192,7 @@ where
189192
let maximum_capacity = maximum_capacity
190193
.max(minimum_capacity.load(Relaxed))
191194
.max(BucketArray::<K, V, DoublyLinkedList, CACHE>::minimum_capacity())
192-
.min(1_usize << (usize::BITS - 1))
195+
.min(MAXIMUM_CAPACITY_LIMIT)
193196
.next_power_of_two();
194197
HashCache {
195198
bucket_array: array,
@@ -219,10 +222,9 @@ where
219222
#[inline]
220223
pub async fn entry_async(&self, key: K) -> Entry<'_, K, V, H> {
221224
let hash = self.hash(&key);
222-
let async_guard = pin!(AsyncGuard::default());
223-
let locked_bucket = self.writer_async(hash, &async_guard).await;
224-
let prolonged_guard = self.prolonged_guard_ref(async_guard.guard());
225-
let entry_ptr = locked_bucket.search(&key, hash, prolonged_guard);
225+
let locked_bucket = self.writer_async(hash).await;
226+
let fake_guard = fake_guard();
227+
let entry_ptr = locked_bucket.search(&key, hash, fake_guard);
226228
if entry_ptr.is_valid() {
227229
Entry::Occupied(OccupiedEntry {
228230
hashcache: self,
@@ -341,14 +343,13 @@ where
341343
#[inline]
342344
pub async fn put_async(&self, key: K, val: V) -> Result<EvictedEntry<K, V>, (K, V)> {
343345
let hash = self.hash(&key);
344-
let async_guard = pin!(AsyncGuard::default());
345-
let locked_bucket = self.writer_async(hash, &async_guard).await;
346-
let guard = async_guard.guard();
347-
if locked_bucket.search(&key, hash, guard).is_valid() {
346+
let locked_bucket = self.writer_async(hash).await;
347+
let fake_guard = fake_guard();
348+
if locked_bucket.search(&key, hash, fake_guard).is_valid() {
348349
Err((key, val))
349350
} else {
350351
let evicted = locked_bucket.evict_lru_head(locked_bucket.data_block);
351-
let entry_ptr = locked_bucket.insert(hash, (key, val), guard);
352+
let entry_ptr = locked_bucket.insert(hash, (key, val), fake_guard);
352353
locked_bucket.update_lru_tail(&entry_ptr);
353354
Ok(evicted)
354355
}
@@ -441,10 +442,9 @@ where
441442
#[inline]
442443
pub async fn replace_async(&self, key: K) -> ReplaceResult<'_, K, V, H> {
443444
let hash = self.hash(&key);
444-
let async_guard = pin!(AsyncGuard::default());
445-
let locked_bucket = self.writer_async(hash, &async_guard).await;
446-
let prolonged_guard = self.prolonged_guard_ref(async_guard.guard());
447-
let mut entry_ptr = locked_bucket.search(&key, hash, prolonged_guard);
445+
let locked_bucket = self.writer_async(hash).await;
446+
let fake_guard = fake_guard();
447+
let mut entry_ptr = locked_bucket.search(&key, hash, fake_guard);
448448
if entry_ptr.is_valid() {
449449
let prev_key = replace(
450450
&mut entry_ptr
@@ -610,10 +610,9 @@ where
610610
Q: Equivalent<K> + Hash + ?Sized,
611611
{
612612
let hash = self.hash(key);
613-
let async_guard = pin!(AsyncGuard::default());
614-
let locked_bucket = self.optional_writer_async(hash, &async_guard).await?;
615-
let prolonged_guard = self.prolonged_guard_ref(async_guard.guard());
616-
let entry_ptr = locked_bucket.search(key, hash, prolonged_guard);
613+
let locked_bucket = self.optional_writer_async(hash).await?;
614+
let fake_guard = fake_guard();
615+
let entry_ptr = locked_bucket.search(key, hash, fake_guard);
617616
if entry_ptr.is_valid() {
618617
locked_bucket.writer.update_lru_tail(&entry_ptr);
619618
return Some(OccupiedEntry {
@@ -686,8 +685,7 @@ where
686685
Q: Equivalent<K> + Hash + ?Sized,
687686
{
688687
let hash = self.hash(key);
689-
let async_guard = pin!(AsyncGuard::default());
690-
self.reader_async(key, hash, reader, &async_guard).await
688+
self.reader_async(key, hash, reader).await
691689
}
692690

693691
/// Reads a key-value pair.
@@ -731,7 +729,8 @@ where
731729
where
732730
Q: Equivalent<K> + Hash + ?Sized,
733731
{
734-
self.read_async(key, |_, _| ()).await.is_some()
732+
let hash = self.hash(key);
733+
self.reader_async(key, hash, |_, _| ()).await.is_some()
735734
}
736735

737736
/// Returns `true` if the [`HashCache`] contains a value for the specified key.
@@ -778,11 +777,11 @@ where
778777
Q: Equivalent<K> + Hash + ?Sized,
779778
{
780779
let hash = self.hash(key);
781-
let async_guard = pin!(AsyncGuard::default());
782-
let mut locked_bucket = self.optional_writer_async(hash, &async_guard).await?;
783-
let mut entry_ptr = locked_bucket.search(key, hash, async_guard.guard());
780+
let mut locked_bucket = self.optional_writer_async(hash).await?;
781+
let fake_guard = fake_guard();
782+
let mut entry_ptr = locked_bucket.search(key, hash, fake_guard);
784783
if entry_ptr.is_valid() && condition(&mut locked_bucket.entry_mut(&mut entry_ptr).1) {
785-
Some(locked_bucket.remove(self, &mut entry_ptr, async_guard.guard()))
784+
Some(locked_bucket.remove(self, &mut entry_ptr, &Guard::new()))
786785
} else {
787786
None
788787
}
@@ -845,12 +844,11 @@ where
845844
/// ```
846845
#[inline]
847846
pub async fn iter_async<F: FnMut(&K, &V) -> bool>(&self, mut f: F) -> bool {
848-
let async_guard = pin!(AsyncGuard::default());
849847
let mut result = true;
850-
self.for_each_reader_async(&async_guard, |reader, data_block| {
851-
let guard = async_guard.guard();
852-
let mut entry_ptr = EntryPtr::new(guard);
853-
while entry_ptr.move_to_next(&reader, guard) {
848+
self.for_each_reader_async(|reader, data_block| {
849+
let fake_guard = fake_guard();
850+
let mut entry_ptr = EntryPtr::new(fake_guard);
851+
while entry_ptr.move_to_next(&reader, fake_guard) {
854852
let (k, v) = entry_ptr.get(data_block);
855853
if !f(k, v) {
856854
result = false;
@@ -937,17 +935,16 @@ where
937935
&self,
938936
mut f: F,
939937
) -> bool {
940-
let async_guard = pin!(AsyncGuard::default());
941938
let mut result = true;
942-
self.for_each_writer_async(0, 0, &async_guard, |mut locked_bucket, removed| {
943-
let guard = async_guard.guard();
944-
let mut entry_ptr = EntryPtr::new(guard);
945-
while entry_ptr.move_to_next(&locked_bucket.writer, guard) {
939+
self.for_each_writer_async(0, 0, |mut locked_bucket, removed| {
940+
let fake_guard = fake_guard();
941+
let mut entry_ptr = EntryPtr::new(fake_guard);
942+
while entry_ptr.move_to_next(&locked_bucket.writer, fake_guard) {
946943
let consumable_entry = ConsumableEntry {
947944
locked_bucket: &mut locked_bucket,
948945
entry_ptr: &mut entry_ptr,
949946
remove_probe: removed,
950-
guard,
947+
guard: fake_guard,
951948
};
952949
if !f(consumable_entry) {
953950
result = false;
@@ -1329,12 +1326,12 @@ where
13291326
}
13301327

13311328
#[inline]
1332-
fn bucket_array(&self) -> &AtomicShared<BucketArray<K, V, DoublyLinkedList, CACHE>> {
1329+
fn bucket_array_var(&self) -> &AtomicShared<BucketArray<K, V, DoublyLinkedList, CACHE>> {
13331330
&self.bucket_array
13341331
}
13351332

13361333
#[inline]
1337-
fn minimum_capacity(&self) -> &AtomicUsize {
1334+
fn minimum_capacity_var(&self) -> &AtomicUsize {
13381335
&self.minimum_capacity
13391336
}
13401337

0 commit comments

Comments
 (0)