Skip to main content

WeightedCache

Struct WeightedCache 

Source
pub struct WeightedCache<K, V> { /* private fields */ }

Implementations§

Source§

impl<K: Hash + Eq + Clone, V> WeightedCache<K, V>

Source

pub fn with_capacity_bytes<F>(capacity_bytes: usize, size_of: F) -> Self
where F: Fn(&V) -> usize + 'static,

Examples found in repository?
examples/sample_app.rs (line 154)
150fn weighted_byte_budget() {
151    use subms_block_cache::WeightedCache;
152    println!("\n== weighted: byte-budgeted page cache ==");
153    let mut cache: WeightedCache<u64, Vec<u8>> =
154        WeightedCache::with_capacity_bytes(4096, |page: &Vec<u8>| page.len());
155
156    let mut evicted_total = 0;
157    for id in 0u64..64 {
158        let size = 128 + (id as usize % 8) * 128;
159        evicted_total += cache.put(id, vec![0u8; size]).len();
160    }
161    println!(
162        "  used {} / 4096 bytes, {} pages, {evicted_total} evicted",
163        cache.used_bytes(),
164        cache.len()
165    );
166    assert!(cache.used_bytes() <= 4096, "byte budget is a hard bound");
167
168    let too_big = cache.put(999, vec![0u8; 8192]);
169    assert_eq!(too_big.len(), 1, "oversized page is rejected, not admitted");
170    assert!(cache.get(&999).is_none());
171}
More examples
Hide additional examples
examples/perf_features.rs (line 188)
82fn main() -> io::Result<()> {
83    let canon = SIZES[SIZES.len() - 1];
84
85    let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
86        .join("..")
87        .join(".subms")
88        .join("features")
89        .join("rust.json");
90    let existing = std::fs::read_to_string(&path).unwrap_or_default();
91    let mut manifest = SubMsFeatureManifest::load_str("rust", &existing);
92    // Stamp the box these numbers came from. The bench runs wherever it is
93    // invoked, so an unstamped manifest is indistinguishable from a fleet
94    // capture; the renderer will not publish one it cannot attribute.
95    let (source, instance) = SubMsP99Source::from_env();
96    manifest.set_p99_source(source, instance.as_deref());
97
98    // ---------- base (clock-sweep): the baseline, not a feature ----------
99    // Every feature is classified against this. A variant whose lookup lands
100    // within a whisker of the base is a capability, not a latency change, and
101    // classify_feature says so rather than calling it hot-path by default.
102    // The baseline is a p50, because the sweep values are p50s. Handing
103    // classify_feature a base p99 against p50 sweep points compares two
104    // different statistics: the p50 sits below the p99 almost by construction,
105    // so every feature reads as "within 10% of base" and lands auxiliary.
106    let base_p50 = {
107        use subms_block_cache::BlockCache;
108        let mut c: BlockCache<u32, u64> = BlockCache::with_capacity(canon);
109        for k in 0..canon as u32 {
110            c.put(k, k as u64);
111        }
112        let (p50, _) = get_hit(canon, |key| c.get(&key).is_some());
113        p50
114    };
115
116    // ---------- arc: adaptive replacement, recency + frequency lists ----------
117    #[cfg(feature = "arc")]
118    {
119        use subms_block_cache::ArcCache;
120        let sweep: Vec<(usize, u64)> = SIZES
121            .iter()
122            .map(|&n| {
123                let mut c: ArcCache<u32, u64> = ArcCache::with_capacity(n);
124                for k in 0..n as u32 {
125                    c.put(k, k as u64);
126                }
127                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
128                (n, p50)
129            })
130            .collect();
131        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
132
133        let mut c: ArcCache<u32, u64> = ArcCache::with_capacity(canon);
134        for k in 0..canon as u32 {
135            c.put(k, k as u64);
136        }
137        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
138        let (_, put99) = put_evicting(canon, |key| {
139            c.put(key, key as u64);
140        });
141        let mut p99 = BTreeMap::new();
142        p99.insert("get_hit".to_string(), get99);
143        p99.insert("put".to_string(), put99);
144        manifest.set_feature("arc", cat, &p99, &reason);
145    }
146
147    // ---------- tinylfu: frequency-sketch admission ----------
148    #[cfg(feature = "tinylfu")]
149    {
150        use subms_block_cache::TinyLfuCache;
151        let sweep: Vec<(usize, u64)> = SIZES
152            .iter()
153            .map(|&n| {
154                let mut c: TinyLfuCache<u32, u64> = TinyLfuCache::with_capacity(n);
155                for k in 0..n as u32 {
156                    c.put(k, k as u64);
157                }
158                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
159                (n, p50)
160            })
161            .collect();
162        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
163
164        let mut c: TinyLfuCache<u32, u64> = TinyLfuCache::with_capacity(canon);
165        for k in 0..canon as u32 {
166            c.put(k, k as u64);
167        }
168        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
169        let (_, put99) = put_evicting(canon, |key| {
170            c.put(key, key as u64);
171        });
172        let mut p99 = BTreeMap::new();
173        p99.insert("get_hit".to_string(), get99);
174        p99.insert("put".to_string(), put99);
175        manifest.set_feature("tinylfu", cat, &p99, &reason);
176    }
177
178    // ---------- weighted: a byte budget rather than a slot count ----------
179    #[cfg(feature = "weighted")]
180    {
181        use subms_block_cache::WeightedCache;
182        // 1 byte per entry so capacity_bytes == slot capacity; eviction behaves
183        // like the base cache, which isolates the weight bookkeeping itself.
184        let sweep: Vec<(usize, u64)> = SIZES
185            .iter()
186            .map(|&n| {
187                let mut c: WeightedCache<u32, u64> =
188                    WeightedCache::with_capacity_bytes(n, |_v: &u64| 1);
189                for k in 0..n as u32 {
190                    c.put(k, k as u64);
191                }
192                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
193                (n, p50)
194            })
195            .collect();
196        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
197
198        let mut c: WeightedCache<u32, u64> =
199            WeightedCache::with_capacity_bytes(canon, |_v: &u64| 1);
200        for k in 0..canon as u32 {
201            c.put(k, k as u64);
202        }
203        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
204        let (_, put99) = put_evicting(canon, |key| {
205            let _ = c.put(key, key as u64);
206        });
207        let mut p99 = BTreeMap::new();
208        p99.insert("get_hit".to_string(), get99);
209        p99.insert("put".to_string(), put99);
210        manifest.set_feature("weighted", cat, &p99, &reason);
211    }
212
213    // ---------- concurrent-shards: measured single-threaded ----------
214    // Uncontended on purpose. This isolates the sharding INDIRECTION from the
215    // contention it exists to relieve; a multi-threaded number here would say
216    // more about the thread count than about the feature.
217    #[cfg(feature = "concurrent-shards")]
218    {
219        use subms_block_cache::ShardedCache;
220        let sweep: Vec<(usize, u64)> = SIZES
221            .iter()
222            .map(|&n| {
223                let c: ShardedCache<u32, u64> = ShardedCache::with_capacity(n, 16);
224                for k in 0..n as u32 {
225                    c.put(k, k as u64);
226                }
227                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
228                (n, p50)
229            })
230            .collect();
231        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
232
233        let c: ShardedCache<u32, u64> = ShardedCache::with_capacity(canon, 16);
234        for k in 0..canon as u32 {
235            c.put(k, k as u64);
236        }
237        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
238        let (_, put99) = put_evicting(canon, |key| {
239            c.put(key, key as u64);
240        });
241        let mut p99 = BTreeMap::new();
242        p99.insert("get_hit".to_string(), get99);
243        p99.insert("put".to_string(), put99);
244        manifest.set_feature("concurrent-shards", cat, &p99, &reason);
245    }
246
247    // ---------- metrics: hit/miss counters on the lookup path ----------
248    #[cfg(feature = "metrics")]
249    {
250        use subms_block_cache::MetricsCache;
251        let sweep: Vec<(usize, u64)> = SIZES
252            .iter()
253            .map(|&n| {
254                let mut c: MetricsCache<u32, u64> = MetricsCache::with_capacity(n);
255                for k in 0..n as u32 {
256                    c.put(k, k as u64);
257                }
258                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
259                (n, p50)
260            })
261            .collect();
262        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
263
264        let mut c: MetricsCache<u32, u64> = MetricsCache::with_capacity(canon);
265        for k in 0..canon as u32 {
266            c.put(k, k as u64);
267        }
268        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
269        let (_, put99) = put_evicting(canon, |key| {
270            c.put(key, key as u64);
271        });
272        let mut p99 = BTreeMap::new();
273        p99.insert("get_hit".to_string(), get99);
274        p99.insert("put".to_string(), put99);
275        manifest.set_feature("metrics", cat, &p99, &reason);
276    }
277
278    std::fs::create_dir_all(path.parent().unwrap())?;
279    std::fs::write(&path, manifest.to_json())?;
280    io::stdout().write_all(manifest.to_json().as_bytes())?;
281    Ok(())
282}
Source

pub fn capacity_bytes(&self) -> usize

Source

pub fn used_bytes(&self) -> usize

Examples found in repository?
examples/sample_app.rs (line 163)
150fn weighted_byte_budget() {
151    use subms_block_cache::WeightedCache;
152    println!("\n== weighted: byte-budgeted page cache ==");
153    let mut cache: WeightedCache<u64, Vec<u8>> =
154        WeightedCache::with_capacity_bytes(4096, |page: &Vec<u8>| page.len());
155
156    let mut evicted_total = 0;
157    for id in 0u64..64 {
158        let size = 128 + (id as usize % 8) * 128;
159        evicted_total += cache.put(id, vec![0u8; size]).len();
160    }
161    println!(
162        "  used {} / 4096 bytes, {} pages, {evicted_total} evicted",
163        cache.used_bytes(),
164        cache.len()
165    );
166    assert!(cache.used_bytes() <= 4096, "byte budget is a hard bound");
167
168    let too_big = cache.put(999, vec![0u8; 8192]);
169    assert_eq!(too_big.len(), 1, "oversized page is rejected, not admitted");
170    assert!(cache.get(&999).is_none());
171}
Source

pub fn len(&self) -> usize

Examples found in repository?
examples/sample_app.rs (line 164)
150fn weighted_byte_budget() {
151    use subms_block_cache::WeightedCache;
152    println!("\n== weighted: byte-budgeted page cache ==");
153    let mut cache: WeightedCache<u64, Vec<u8>> =
154        WeightedCache::with_capacity_bytes(4096, |page: &Vec<u8>| page.len());
155
156    let mut evicted_total = 0;
157    for id in 0u64..64 {
158        let size = 128 + (id as usize % 8) * 128;
159        evicted_total += cache.put(id, vec![0u8; size]).len();
160    }
161    println!(
162        "  used {} / 4096 bytes, {} pages, {evicted_total} evicted",
163        cache.used_bytes(),
164        cache.len()
165    );
166    assert!(cache.used_bytes() <= 4096, "byte budget is a hard bound");
167
168    let too_big = cache.put(999, vec![0u8; 8192]);
169    assert_eq!(too_big.len(), 1, "oversized page is rejected, not admitted");
170    assert!(cache.get(&999).is_none());
171}
Source

pub fn is_empty(&self) -> bool

Source

pub fn get(&mut self, key: &K) -> Option<&V>

Examples found in repository?
examples/sample_app.rs (line 170)
150fn weighted_byte_budget() {
151    use subms_block_cache::WeightedCache;
152    println!("\n== weighted: byte-budgeted page cache ==");
153    let mut cache: WeightedCache<u64, Vec<u8>> =
154        WeightedCache::with_capacity_bytes(4096, |page: &Vec<u8>| page.len());
155
156    let mut evicted_total = 0;
157    for id in 0u64..64 {
158        let size = 128 + (id as usize % 8) * 128;
159        evicted_total += cache.put(id, vec![0u8; size]).len();
160    }
161    println!(
162        "  used {} / 4096 bytes, {} pages, {evicted_total} evicted",
163        cache.used_bytes(),
164        cache.len()
165    );
166    assert!(cache.used_bytes() <= 4096, "byte budget is a hard bound");
167
168    let too_big = cache.put(999, vec![0u8; 8192]);
169    assert_eq!(too_big.len(), 1, "oversized page is rejected, not admitted");
170    assert!(cache.get(&999).is_none());
171}
More examples
Hide additional examples
examples/perf_features.rs (line 192)
82fn main() -> io::Result<()> {
83    let canon = SIZES[SIZES.len() - 1];
84
85    let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
86        .join("..")
87        .join(".subms")
88        .join("features")
89        .join("rust.json");
90    let existing = std::fs::read_to_string(&path).unwrap_or_default();
91    let mut manifest = SubMsFeatureManifest::load_str("rust", &existing);
92    // Stamp the box these numbers came from. The bench runs wherever it is
93    // invoked, so an unstamped manifest is indistinguishable from a fleet
94    // capture; the renderer will not publish one it cannot attribute.
95    let (source, instance) = SubMsP99Source::from_env();
96    manifest.set_p99_source(source, instance.as_deref());
97
98    // ---------- base (clock-sweep): the baseline, not a feature ----------
99    // Every feature is classified against this. A variant whose lookup lands
100    // within a whisker of the base is a capability, not a latency change, and
101    // classify_feature says so rather than calling it hot-path by default.
102    // The baseline is a p50, because the sweep values are p50s. Handing
103    // classify_feature a base p99 against p50 sweep points compares two
104    // different statistics: the p50 sits below the p99 almost by construction,
105    // so every feature reads as "within 10% of base" and lands auxiliary.
106    let base_p50 = {
107        use subms_block_cache::BlockCache;
108        let mut c: BlockCache<u32, u64> = BlockCache::with_capacity(canon);
109        for k in 0..canon as u32 {
110            c.put(k, k as u64);
111        }
112        let (p50, _) = get_hit(canon, |key| c.get(&key).is_some());
113        p50
114    };
115
116    // ---------- arc: adaptive replacement, recency + frequency lists ----------
117    #[cfg(feature = "arc")]
118    {
119        use subms_block_cache::ArcCache;
120        let sweep: Vec<(usize, u64)> = SIZES
121            .iter()
122            .map(|&n| {
123                let mut c: ArcCache<u32, u64> = ArcCache::with_capacity(n);
124                for k in 0..n as u32 {
125                    c.put(k, k as u64);
126                }
127                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
128                (n, p50)
129            })
130            .collect();
131        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
132
133        let mut c: ArcCache<u32, u64> = ArcCache::with_capacity(canon);
134        for k in 0..canon as u32 {
135            c.put(k, k as u64);
136        }
137        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
138        let (_, put99) = put_evicting(canon, |key| {
139            c.put(key, key as u64);
140        });
141        let mut p99 = BTreeMap::new();
142        p99.insert("get_hit".to_string(), get99);
143        p99.insert("put".to_string(), put99);
144        manifest.set_feature("arc", cat, &p99, &reason);
145    }
146
147    // ---------- tinylfu: frequency-sketch admission ----------
148    #[cfg(feature = "tinylfu")]
149    {
150        use subms_block_cache::TinyLfuCache;
151        let sweep: Vec<(usize, u64)> = SIZES
152            .iter()
153            .map(|&n| {
154                let mut c: TinyLfuCache<u32, u64> = TinyLfuCache::with_capacity(n);
155                for k in 0..n as u32 {
156                    c.put(k, k as u64);
157                }
158                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
159                (n, p50)
160            })
161            .collect();
162        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
163
164        let mut c: TinyLfuCache<u32, u64> = TinyLfuCache::with_capacity(canon);
165        for k in 0..canon as u32 {
166            c.put(k, k as u64);
167        }
168        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
169        let (_, put99) = put_evicting(canon, |key| {
170            c.put(key, key as u64);
171        });
172        let mut p99 = BTreeMap::new();
173        p99.insert("get_hit".to_string(), get99);
174        p99.insert("put".to_string(), put99);
175        manifest.set_feature("tinylfu", cat, &p99, &reason);
176    }
177
178    // ---------- weighted: a byte budget rather than a slot count ----------
179    #[cfg(feature = "weighted")]
180    {
181        use subms_block_cache::WeightedCache;
182        // 1 byte per entry so capacity_bytes == slot capacity; eviction behaves
183        // like the base cache, which isolates the weight bookkeeping itself.
184        let sweep: Vec<(usize, u64)> = SIZES
185            .iter()
186            .map(|&n| {
187                let mut c: WeightedCache<u32, u64> =
188                    WeightedCache::with_capacity_bytes(n, |_v: &u64| 1);
189                for k in 0..n as u32 {
190                    c.put(k, k as u64);
191                }
192                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
193                (n, p50)
194            })
195            .collect();
196        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
197
198        let mut c: WeightedCache<u32, u64> =
199            WeightedCache::with_capacity_bytes(canon, |_v: &u64| 1);
200        for k in 0..canon as u32 {
201            c.put(k, k as u64);
202        }
203        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
204        let (_, put99) = put_evicting(canon, |key| {
205            let _ = c.put(key, key as u64);
206        });
207        let mut p99 = BTreeMap::new();
208        p99.insert("get_hit".to_string(), get99);
209        p99.insert("put".to_string(), put99);
210        manifest.set_feature("weighted", cat, &p99, &reason);
211    }
212
213    // ---------- concurrent-shards: measured single-threaded ----------
214    // Uncontended on purpose. This isolates the sharding INDIRECTION from the
215    // contention it exists to relieve; a multi-threaded number here would say
216    // more about the thread count than about the feature.
217    #[cfg(feature = "concurrent-shards")]
218    {
219        use subms_block_cache::ShardedCache;
220        let sweep: Vec<(usize, u64)> = SIZES
221            .iter()
222            .map(|&n| {
223                let c: ShardedCache<u32, u64> = ShardedCache::with_capacity(n, 16);
224                for k in 0..n as u32 {
225                    c.put(k, k as u64);
226                }
227                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
228                (n, p50)
229            })
230            .collect();
231        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
232
233        let c: ShardedCache<u32, u64> = ShardedCache::with_capacity(canon, 16);
234        for k in 0..canon as u32 {
235            c.put(k, k as u64);
236        }
237        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
238        let (_, put99) = put_evicting(canon, |key| {
239            c.put(key, key as u64);
240        });
241        let mut p99 = BTreeMap::new();
242        p99.insert("get_hit".to_string(), get99);
243        p99.insert("put".to_string(), put99);
244        manifest.set_feature("concurrent-shards", cat, &p99, &reason);
245    }
246
247    // ---------- metrics: hit/miss counters on the lookup path ----------
248    #[cfg(feature = "metrics")]
249    {
250        use subms_block_cache::MetricsCache;
251        let sweep: Vec<(usize, u64)> = SIZES
252            .iter()
253            .map(|&n| {
254                let mut c: MetricsCache<u32, u64> = MetricsCache::with_capacity(n);
255                for k in 0..n as u32 {
256                    c.put(k, k as u64);
257                }
258                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
259                (n, p50)
260            })
261            .collect();
262        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
263
264        let mut c: MetricsCache<u32, u64> = MetricsCache::with_capacity(canon);
265        for k in 0..canon as u32 {
266            c.put(k, k as u64);
267        }
268        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
269        let (_, put99) = put_evicting(canon, |key| {
270            c.put(key, key as u64);
271        });
272        let mut p99 = BTreeMap::new();
273        p99.insert("get_hit".to_string(), get99);
274        p99.insert("put".to_string(), put99);
275        manifest.set_feature("metrics", cat, &p99, &reason);
276    }
277
278    std::fs::create_dir_all(path.parent().unwrap())?;
279    std::fs::write(&path, manifest.to_json())?;
280    io::stdout().write_all(manifest.to_json().as_bytes())?;
281    Ok(())
282}
Source

pub fn put(&mut self, key: K, value: V) -> Vec<(K, V)>

Insert or update. May evict multiple entries to fit the new one. Returns a Vec of evicted (key, value) pairs in eviction order; empty if nothing was evicted.

Examples found in repository?
examples/sample_app.rs (line 159)
150fn weighted_byte_budget() {
151    use subms_block_cache::WeightedCache;
152    println!("\n== weighted: byte-budgeted page cache ==");
153    let mut cache: WeightedCache<u64, Vec<u8>> =
154        WeightedCache::with_capacity_bytes(4096, |page: &Vec<u8>| page.len());
155
156    let mut evicted_total = 0;
157    for id in 0u64..64 {
158        let size = 128 + (id as usize % 8) * 128;
159        evicted_total += cache.put(id, vec![0u8; size]).len();
160    }
161    println!(
162        "  used {} / 4096 bytes, {} pages, {evicted_total} evicted",
163        cache.used_bytes(),
164        cache.len()
165    );
166    assert!(cache.used_bytes() <= 4096, "byte budget is a hard bound");
167
168    let too_big = cache.put(999, vec![0u8; 8192]);
169    assert_eq!(too_big.len(), 1, "oversized page is rejected, not admitted");
170    assert!(cache.get(&999).is_none());
171}
More examples
Hide additional examples
examples/perf_features.rs (line 190)
82fn main() -> io::Result<()> {
83    let canon = SIZES[SIZES.len() - 1];
84
85    let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
86        .join("..")
87        .join(".subms")
88        .join("features")
89        .join("rust.json");
90    let existing = std::fs::read_to_string(&path).unwrap_or_default();
91    let mut manifest = SubMsFeatureManifest::load_str("rust", &existing);
92    // Stamp the box these numbers came from. The bench runs wherever it is
93    // invoked, so an unstamped manifest is indistinguishable from a fleet
94    // capture; the renderer will not publish one it cannot attribute.
95    let (source, instance) = SubMsP99Source::from_env();
96    manifest.set_p99_source(source, instance.as_deref());
97
98    // ---------- base (clock-sweep): the baseline, not a feature ----------
99    // Every feature is classified against this. A variant whose lookup lands
100    // within a whisker of the base is a capability, not a latency change, and
101    // classify_feature says so rather than calling it hot-path by default.
102    // The baseline is a p50, because the sweep values are p50s. Handing
103    // classify_feature a base p99 against p50 sweep points compares two
104    // different statistics: the p50 sits below the p99 almost by construction,
105    // so every feature reads as "within 10% of base" and lands auxiliary.
106    let base_p50 = {
107        use subms_block_cache::BlockCache;
108        let mut c: BlockCache<u32, u64> = BlockCache::with_capacity(canon);
109        for k in 0..canon as u32 {
110            c.put(k, k as u64);
111        }
112        let (p50, _) = get_hit(canon, |key| c.get(&key).is_some());
113        p50
114    };
115
116    // ---------- arc: adaptive replacement, recency + frequency lists ----------
117    #[cfg(feature = "arc")]
118    {
119        use subms_block_cache::ArcCache;
120        let sweep: Vec<(usize, u64)> = SIZES
121            .iter()
122            .map(|&n| {
123                let mut c: ArcCache<u32, u64> = ArcCache::with_capacity(n);
124                for k in 0..n as u32 {
125                    c.put(k, k as u64);
126                }
127                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
128                (n, p50)
129            })
130            .collect();
131        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
132
133        let mut c: ArcCache<u32, u64> = ArcCache::with_capacity(canon);
134        for k in 0..canon as u32 {
135            c.put(k, k as u64);
136        }
137        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
138        let (_, put99) = put_evicting(canon, |key| {
139            c.put(key, key as u64);
140        });
141        let mut p99 = BTreeMap::new();
142        p99.insert("get_hit".to_string(), get99);
143        p99.insert("put".to_string(), put99);
144        manifest.set_feature("arc", cat, &p99, &reason);
145    }
146
147    // ---------- tinylfu: frequency-sketch admission ----------
148    #[cfg(feature = "tinylfu")]
149    {
150        use subms_block_cache::TinyLfuCache;
151        let sweep: Vec<(usize, u64)> = SIZES
152            .iter()
153            .map(|&n| {
154                let mut c: TinyLfuCache<u32, u64> = TinyLfuCache::with_capacity(n);
155                for k in 0..n as u32 {
156                    c.put(k, k as u64);
157                }
158                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
159                (n, p50)
160            })
161            .collect();
162        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
163
164        let mut c: TinyLfuCache<u32, u64> = TinyLfuCache::with_capacity(canon);
165        for k in 0..canon as u32 {
166            c.put(k, k as u64);
167        }
168        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
169        let (_, put99) = put_evicting(canon, |key| {
170            c.put(key, key as u64);
171        });
172        let mut p99 = BTreeMap::new();
173        p99.insert("get_hit".to_string(), get99);
174        p99.insert("put".to_string(), put99);
175        manifest.set_feature("tinylfu", cat, &p99, &reason);
176    }
177
178    // ---------- weighted: a byte budget rather than a slot count ----------
179    #[cfg(feature = "weighted")]
180    {
181        use subms_block_cache::WeightedCache;
182        // 1 byte per entry so capacity_bytes == slot capacity; eviction behaves
183        // like the base cache, which isolates the weight bookkeeping itself.
184        let sweep: Vec<(usize, u64)> = SIZES
185            .iter()
186            .map(|&n| {
187                let mut c: WeightedCache<u32, u64> =
188                    WeightedCache::with_capacity_bytes(n, |_v: &u64| 1);
189                for k in 0..n as u32 {
190                    c.put(k, k as u64);
191                }
192                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
193                (n, p50)
194            })
195            .collect();
196        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
197
198        let mut c: WeightedCache<u32, u64> =
199            WeightedCache::with_capacity_bytes(canon, |_v: &u64| 1);
200        for k in 0..canon as u32 {
201            c.put(k, k as u64);
202        }
203        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
204        let (_, put99) = put_evicting(canon, |key| {
205            let _ = c.put(key, key as u64);
206        });
207        let mut p99 = BTreeMap::new();
208        p99.insert("get_hit".to_string(), get99);
209        p99.insert("put".to_string(), put99);
210        manifest.set_feature("weighted", cat, &p99, &reason);
211    }
212
213    // ---------- concurrent-shards: measured single-threaded ----------
214    // Uncontended on purpose. This isolates the sharding INDIRECTION from the
215    // contention it exists to relieve; a multi-threaded number here would say
216    // more about the thread count than about the feature.
217    #[cfg(feature = "concurrent-shards")]
218    {
219        use subms_block_cache::ShardedCache;
220        let sweep: Vec<(usize, u64)> = SIZES
221            .iter()
222            .map(|&n| {
223                let c: ShardedCache<u32, u64> = ShardedCache::with_capacity(n, 16);
224                for k in 0..n as u32 {
225                    c.put(k, k as u64);
226                }
227                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
228                (n, p50)
229            })
230            .collect();
231        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
232
233        let c: ShardedCache<u32, u64> = ShardedCache::with_capacity(canon, 16);
234        for k in 0..canon as u32 {
235            c.put(k, k as u64);
236        }
237        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
238        let (_, put99) = put_evicting(canon, |key| {
239            c.put(key, key as u64);
240        });
241        let mut p99 = BTreeMap::new();
242        p99.insert("get_hit".to_string(), get99);
243        p99.insert("put".to_string(), put99);
244        manifest.set_feature("concurrent-shards", cat, &p99, &reason);
245    }
246
247    // ---------- metrics: hit/miss counters on the lookup path ----------
248    #[cfg(feature = "metrics")]
249    {
250        use subms_block_cache::MetricsCache;
251        let sweep: Vec<(usize, u64)> = SIZES
252            .iter()
253            .map(|&n| {
254                let mut c: MetricsCache<u32, u64> = MetricsCache::with_capacity(n);
255                for k in 0..n as u32 {
256                    c.put(k, k as u64);
257                }
258                let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
259                (n, p50)
260            })
261            .collect();
262        let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
263
264        let mut c: MetricsCache<u32, u64> = MetricsCache::with_capacity(canon);
265        for k in 0..canon as u32 {
266            c.put(k, k as u64);
267        }
268        let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
269        let (_, put99) = put_evicting(canon, |key| {
270            c.put(key, key as u64);
271        });
272        let mut p99 = BTreeMap::new();
273        p99.insert("get_hit".to_string(), get99);
274        p99.insert("put".to_string(), put99);
275        manifest.set_feature("metrics", cat, &p99, &reason);
276    }
277
278    std::fs::create_dir_all(path.parent().unwrap())?;
279    std::fs::write(&path, manifest.to_json())?;
280    io::stdout().write_all(manifest.to_json().as_bytes())?;
281    Ok(())
282}

Auto Trait Implementations§

§

impl<K, V> !RefUnwindSafe for WeightedCache<K, V>

§

impl<K, V> !Send for WeightedCache<K, V>

§

impl<K, V> !Sync for WeightedCache<K, V>

§

impl<K, V> !UnwindSafe for WeightedCache<K, V>

§

impl<K, V> Freeze for WeightedCache<K, V>

§

impl<K, V> Unpin for WeightedCache<K, V>
where K: Unpin, V: Unpin,

§

impl<K, V> UnsafeUnpin for WeightedCache<K, V>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.