From 44cd81a5b2c12ab9efb55a7ed1f325354ef862f8 Mon Sep 17 00:00:00 2001 From: leiysky Date: Wed, 28 Oct 2026 16:45:11 +0800 Subject: [PATCH] perf: skip atomic clears for zero heat bits --- CHANGELOG.md | 1 + cache2/src/region/index/mod.rs | 60 +++++++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3db3e05..ec22b6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ ### Improvements +- L2 index updates and cold-record reclamation skip atomic writes for heat bits that are already clear. - Hot-record reclamation reuses the checksum computed during payload validation for its rewrite, avoiding a second payload scan while preserving record validation and the on-disk format. - L1 replacement and eviction reuse detached, exclusively owned allocations of the same size under CLOCK and S3-FIFO, reducing repeated allocation during fixed-size turnover without an additional buffer pool. Caller-held values remain immutable and retain their separate memory charge. When activity statistics are enabled, mutations with no eviction also avoid a shared zero-value counter update. diff --git a/cache2/src/region/index/mod.rs b/cache2/src/region/index/mod.rs index 43573a7..99bfb36 100644 --- a/cache2/src/region/index/mod.rs +++ b/cache2/src/region/index/mod.rs @@ -106,11 +106,16 @@ impl SlotHeat { fn take_hot(&self, slot: usize) -> bool { let word = slot / REFERENCE_WORD_BITS; let mask = 1_u64 << (slot % REFERENCE_WORD_BITS); - let was_hot = self - .hot - .get(word) - .is_some_and(|current| current.fetch_and(!mask, Ordering::Relaxed) & mask != 0); - if let Some(current) = self.seen.get(word) { + // The caller holds this slot's partition write guard, so its bits + // cannot be marked concurrently. Other partitions may share the word: + // skip already-clear bits, but retain the RMW for actual clears. + let was_hot = self.hot.get(word).is_some_and(|current| { + current.load(Ordering::Relaxed) & mask != 0 + && current.fetch_and(!mask, Ordering::Relaxed) & mask != 0 + }); + if let Some(current) = self.seen.get(word) + && current.load(Ordering::Relaxed) & mask != 0 + { current.fetch_and(!mask, Ordering::Relaxed); } was_hot @@ -720,6 +725,35 @@ mod tests { ); } + #[test] + fn heat_clears_preserve_neighboring_slots_in_a_shared_word() { + let heat = SlotHeat::try_new(REFERENCE_WORD_BITS).unwrap(); + let barrier = std::sync::Barrier::new(2); + + std::thread::scope(|scope| { + for slot in [0, REFERENCE_WORD_BITS - 1] { + let heat = &heat; + let barrier = &barrier; + scope.spawn(move || { + let mut valid = true; + for _ in 0..256 { + heat.mark(slot); + heat.mark(slot); + barrier.wait(); + valid &= heat.take_hot(slot); + barrier.wait(); + valid &= heat.seen[0].load(Ordering::Relaxed) == 0; + valid &= heat.hot[0].load(Ordering::Relaxed) == 0; + heat.clear(slot); + barrier.wait(); + } + // Finish all barriers before reporting a failure. + assert!(valid, "shared-word clears lost or retained a heat bit"); + }); + } + }); + } + #[test] fn point_operations_use_fixed_candidates_without_tombstones() { let index = anonymous(128); @@ -817,13 +851,15 @@ mod tests { ReclaimIndexAction::Removed ); - index.upsert(hash, old).unwrap(); - assert_eq!(index.lookup_raw(hash).unwrap(), Some(old)); - assert_eq!( - index.prepare_reclaim(hash, old.location).unwrap(), - ReclaimIndexAction::Removed, - "one candidate access must not survive a scan" - ); + for _ in 0..2 { + index.upsert(hash, old).unwrap(); + assert_eq!(index.lookup_raw(hash).unwrap(), Some(old)); + assert_eq!( + index.prepare_reclaim(hash, old.location).unwrap(), + ReclaimIndexAction::Removed, + "one candidate access must not survive a scan, even after slot reuse" + ); + } index.upsert(hash, old).unwrap(); assert_eq!(index.lookup_raw(hash).unwrap(), Some(old));