Skip to content

Commit e313874

Browse files
authored
fix: update scan resistance chart and improve documentation clarity (#109)
- Adjusted the maximum value for the scan resistance chart to dynamically reflect the highest score, enhancing visual accuracy. - Revised documentation in `render_docs.rs` to clarify the performance characteristics of cache policies, specifically noting that low latency operations have near O(1) average overhead. - Enhanced comments in `hashmap.rs` and `slab.rs` to provide clearer explanations of panic conditions and invariants, improving overall code maintainability. These changes improve the observability of cache performance metrics and enhance the clarity of documentation for better user understanding.
1 parent 289bcf0 commit e313874

5 files changed

Lines changed: 29 additions & 10 deletions

File tree

bench-support/src/bin/charts_template.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ <h2>Adaptation Speed (Ops to 80%)</h2>
461461
scores.push(result.metrics.scan_resistance.resistance_score);
462462
}
463463
});
464+
const scanResistanceMax = Math.max(1.0, ...scores) * 1.05;
464465

465466
new Chart(document.getElementById('scanResistanceChart'), {
466467
type: 'bar',
@@ -481,7 +482,7 @@ <h2>Adaptation Speed (Ops to 80%)</h2>
481482
scales: {
482483
x: {
483484
beginAtZero: true,
484-
max: 1.0,
485+
max: scanResistanceMax,
485486
title: {
486487
display: true,
487488
text: 'Score (1.0 = perfect)'

bench-support/src/bin/render_docs.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,9 @@ fn generate_policy_guide() -> String {
423423
md.push_str(
424424
"| **Scan-heavy workloads** | S3-FIFO, Heap-LFU | Scan-resistant, protect hot entries |\n",
425425
);
426-
md.push_str("| **Low latency required** | LRU, Clock | Fastest operations, O(1) overhead |\n");
426+
md.push_str(
427+
"| **Low latency required** | LRU, Clock | Fast operations, near O(1) average overhead |\n",
428+
);
427429
md.push_str("| **Memory constrained** | LRU, Clock | Minimal metadata overhead |\n");
428430
md.push_str("| **Frequency-aware** | LFU, Heap-LFU, LRU-K | Track access frequency for better decisions |\n");
429431
md.push_str("| **Shifting patterns** | S3-FIFO, 2Q | Adapt to changing access patterns |\n");

src/store/hashmap.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,9 @@ where
12081208
}
12091209

12101210
/// Computes the shard index for a key.
1211+
///
1212+
/// Sharded operations pay one extra hash to choose the target shard before
1213+
/// the within-shard `HashMap` lookup.
12111214
fn shard_index(&self, key: &K) -> usize {
12121215
(self.hasher.hash_one(key) as usize) % self.shards.len()
12131216
}
@@ -1330,7 +1333,12 @@ where
13301333
}
13311334
if self
13321335
.size
1333-
.compare_exchange(current, current + 1, Ordering::AcqRel, Ordering::Relaxed)
1336+
.compare_exchange_weak(
1337+
current,
1338+
current + 1,
1339+
Ordering::AcqRel,
1340+
Ordering::Relaxed,
1341+
)
13341342
.is_ok()
13351343
{
13361344
break;
@@ -1342,6 +1350,8 @@ where
13421350
struct SizeGuard<'a>(&'a AtomicUsize);
13431351
impl Drop for SizeGuard<'_> {
13441352
fn drop(&mut self) {
1353+
// `size` is only a capacity counter; shard locks guard
1354+
// entries, so rollback does not publish data.
13451355
self.0.fetch_sub(1, Ordering::Relaxed);
13461356
}
13471357
}

src/store/slab.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,9 @@ where
759759
// `panic = "unwind"` build). Assert loudly in debug; in release,
760760
// heal by dropping the stale index entry so we fall into the
761761
// new-key path without panicking.
762-
debug_assert!(false, "slab store index/entries desync at slot {}", id.0);
762+
#[cfg(debug_assertions)]
763+
panic!("slab store index/entries desync at slot {}", id.0);
764+
#[cfg(not(debug_assertions))]
763765
self.index.remove(&key);
764766
}
765767

@@ -1245,11 +1247,12 @@ where
12451247
// or out of bounds. Assert loudly in debug; in release, heal by
12461248
// dropping the stale index entry so the new-key path can take
12471249
// over without blindly counting a logical-update as an insert.
1248-
debug_assert!(
1249-
false,
1250+
#[cfg(debug_assertions)]
1251+
panic!(
12501252
"concurrent slab store index/entries desync at slot {}",
12511253
id.0
12521254
);
1255+
#[cfg(not(debug_assertions))]
12531256
inner.index.remove(&key);
12541257
}
12551258

src/store/weight.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
//!
103103
//! **Costs:**
104104
//! - Weight function called on every insert/update
105-
//! - Uses `Arc<V>` even for single-threaded store (needed for weight_fn)
105+
//! - Uses `Arc<V>` even for single-threaded store (cheap shared ownership for returned values)
106106
//! - Slightly more state to track than simple HashMap stores
107107
//!
108108
//! ## When to Use
@@ -299,7 +299,8 @@ impl StoreCounters {
299299
///
300300
/// Enforces both a maximum number of entries and a maximum total weight.
301301
/// Weight is computed via a caller-provided function and cached per entry.
302-
/// Uses `Arc<V>` for values to enable weight function access.
302+
/// Uses `Arc<V>` for values to allow cheap shared ownership without requiring
303+
/// `V: Clone`.
303304
///
304305
/// # Type Parameters
305306
///
@@ -446,6 +447,8 @@ where
446447
capacity_weight: usize,
447448
weight_fn: F,
448449
) -> Result<Self, std::collections::TryReserveError> {
450+
// Build the map empty, then reserve fallibly so allocation errors are
451+
// returned instead of panicking in an infallible capacity constructor.
449452
let mut map: FxHashMap<K, WeightEntry<V>> = FxHashMap::with_hasher(Default::default());
450453
map.try_reserve(capacity_entries)?;
451454
Ok(Self {
@@ -739,7 +742,7 @@ where
739742
let base_total = self
740743
.total_weight
741744
.checked_sub(entry.weight)
742-
.expect("WeightStore invariant violated: checked_sub failed after invariant check");
745+
.expect("WeightStore invariant violated: total_weight must be >= entry.weight");
743746
let next_total = base_total.checked_add(new_weight).ok_or(StoreFull)?;
744747
if next_total > self.capacity_weight {
745748
return Err(StoreFull);
@@ -806,7 +809,7 @@ where
806809
self.total_weight = self
807810
.total_weight
808811
.checked_sub(entry.weight)
809-
.expect("WeightStore invariant violated: checked_sub failed after invariant check");
812+
.expect("WeightStore invariant violated: total_weight must be >= entry.weight");
810813
self.metrics.inc_remove();
811814
Some(entry.value)
812815
}

0 commit comments

Comments
 (0)