use std::collections::BTreeMap;
use std::io::{self, Write};
use std::path::PathBuf;
use subms::{
SubMsFeatureManifest, SubMsLcg, SubMsP99Source, SubMsPerfHarness, classify_feature, summarize,
};
const SIZES: [usize; 3] = [4_096, 32_768, 262_144];
const SEED: u64 = 0;
fn stage_stats(h: &SubMsPerfHarness, name: &str) -> (u64, u64) {
summarize(h)
.stages
.iter()
.find(|s| s.name == name)
.map_or((0, 0), |s| (s.p50_ns, s.p99_ns))
}
fn get_hit(n: usize, mut get: impl FnMut(u32) -> bool) -> (u64, u64) {
let mut rng = SubMsLcg::new(SEED);
let mut h = SubMsPerfHarness::new("block-cache-feature", "rust");
{
let st = h.stage("op", n);
for _ in 0..n {
let key = rng.next_u32() % (n as u32);
st.time(|| {
let _ = get(key);
});
}
}
stage_stats(&h, "op")
}
fn put_evicting(n: usize, mut put: impl FnMut(u32)) -> (u64, u64) {
let base = n as u32;
let mut h = SubMsPerfHarness::new("block-cache-feature", "rust");
{
let st = h.stage("op", n);
for i in 0..n {
let key = base + i as u32;
st.time(|| put(key));
}
}
stage_stats(&h, "op")
}
fn main() -> io::Result<()> {
let canon = SIZES[SIZES.len() - 1];
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("..")
.join(".subms")
.join("features")
.join("rust.json");
let existing = std::fs::read_to_string(&path).unwrap_or_default();
let mut manifest = SubMsFeatureManifest::load_str("rust", &existing);
let (source, instance) = SubMsP99Source::from_env();
manifest.set_p99_source(source, instance.as_deref());
let base_p50 = {
use subms_block_cache::BlockCache;
let mut c: BlockCache<u32, u64> = BlockCache::with_capacity(canon);
for k in 0..canon as u32 {
c.put(k, k as u64);
}
let (p50, _) = get_hit(canon, |key| c.get(&key).is_some());
p50
};
#[cfg(feature = "arc")]
{
use subms_block_cache::ArcCache;
let sweep: Vec<(usize, u64)> = SIZES
.iter()
.map(|&n| {
let mut c: ArcCache<u32, u64> = ArcCache::with_capacity(n);
for k in 0..n as u32 {
c.put(k, k as u64);
}
let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
(n, p50)
})
.collect();
let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
let mut c: ArcCache<u32, u64> = ArcCache::with_capacity(canon);
for k in 0..canon as u32 {
c.put(k, k as u64);
}
let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
let (_, put99) = put_evicting(canon, |key| {
c.put(key, key as u64);
});
let mut p99 = BTreeMap::new();
p99.insert("get_hit".to_string(), get99);
p99.insert("put".to_string(), put99);
manifest.set_feature("arc", cat, &p99, &reason);
}
#[cfg(feature = "tinylfu")]
{
use subms_block_cache::TinyLfuCache;
let sweep: Vec<(usize, u64)> = SIZES
.iter()
.map(|&n| {
let mut c: TinyLfuCache<u32, u64> = TinyLfuCache::with_capacity(n);
for k in 0..n as u32 {
c.put(k, k as u64);
}
let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
(n, p50)
})
.collect();
let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
let mut c: TinyLfuCache<u32, u64> = TinyLfuCache::with_capacity(canon);
for k in 0..canon as u32 {
c.put(k, k as u64);
}
let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
let (_, put99) = put_evicting(canon, |key| {
c.put(key, key as u64);
});
let mut p99 = BTreeMap::new();
p99.insert("get_hit".to_string(), get99);
p99.insert("put".to_string(), put99);
manifest.set_feature("tinylfu", cat, &p99, &reason);
}
#[cfg(feature = "weighted")]
{
use subms_block_cache::WeightedCache;
let sweep: Vec<(usize, u64)> = SIZES
.iter()
.map(|&n| {
let mut c: WeightedCache<u32, u64> =
WeightedCache::with_capacity_bytes(n, |_v: &u64| 1);
for k in 0..n as u32 {
c.put(k, k as u64);
}
let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
(n, p50)
})
.collect();
let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
let mut c: WeightedCache<u32, u64> =
WeightedCache::with_capacity_bytes(canon, |_v: &u64| 1);
for k in 0..canon as u32 {
c.put(k, k as u64);
}
let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
let (_, put99) = put_evicting(canon, |key| {
let _ = c.put(key, key as u64);
});
let mut p99 = BTreeMap::new();
p99.insert("get_hit".to_string(), get99);
p99.insert("put".to_string(), put99);
manifest.set_feature("weighted", cat, &p99, &reason);
}
#[cfg(feature = "concurrent-shards")]
{
use subms_block_cache::ShardedCache;
let sweep: Vec<(usize, u64)> = SIZES
.iter()
.map(|&n| {
let c: ShardedCache<u32, u64> = ShardedCache::with_capacity(n, 16);
for k in 0..n as u32 {
c.put(k, k as u64);
}
let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
(n, p50)
})
.collect();
let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
let c: ShardedCache<u32, u64> = ShardedCache::with_capacity(canon, 16);
for k in 0..canon as u32 {
c.put(k, k as u64);
}
let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
let (_, put99) = put_evicting(canon, |key| {
c.put(key, key as u64);
});
let mut p99 = BTreeMap::new();
p99.insert("get_hit".to_string(), get99);
p99.insert("put".to_string(), put99);
manifest.set_feature("concurrent-shards", cat, &p99, &reason);
}
#[cfg(feature = "metrics")]
{
use subms_block_cache::MetricsCache;
let sweep: Vec<(usize, u64)> = SIZES
.iter()
.map(|&n| {
let mut c: MetricsCache<u32, u64> = MetricsCache::with_capacity(n);
for k in 0..n as u32 {
c.put(k, k as u64);
}
let (p50, _) = get_hit(n, |key| c.get(&key).is_some());
(n, p50)
})
.collect();
let (cat, reason) = classify_feature(&sweep, Some(base_p50), None);
let mut c: MetricsCache<u32, u64> = MetricsCache::with_capacity(canon);
for k in 0..canon as u32 {
c.put(k, k as u64);
}
let (_, get99) = get_hit(canon, |key| c.get(&key).is_some());
let (_, put99) = put_evicting(canon, |key| {
c.put(key, key as u64);
});
let mut p99 = BTreeMap::new();
p99.insert("get_hit".to_string(), get99);
p99.insert("put".to_string(), put99);
manifest.set_feature("metrics", cat, &p99, &reason);
}
std::fs::create_dir_all(path.parent().unwrap())?;
std::fs::write(&path, manifest.to_json())?;
io::stdout().write_all(manifest.to_json().as_bytes())?;
Ok(())
}