1use std::collections::BTreeMap;
12use std::io::{self, Write};
13use std::path::PathBuf;
14
15use subms::{SubMsFeatureManifest, SubMsP99Source, SubMsPerfHarness, classify_feature, summarize};
16#[allow(unused_imports)]
17use subms_bloom_filter::BloomFilter;
18
19const SIZES: [usize; 3] = [4_096, 32_768, 262_144];
23
24fn keys() -> Vec<String> {
25 (0..SIZES[SIZES.len() - 1])
26 .map(|i| format!("key-{i}"))
27 .collect()
28}
29
30fn probe_p99<T>(
32 size: usize,
33 build: impl Fn() -> T,
34 probe: impl Fn(&T, &str),
35 ks: &[String],
36) -> u64 {
37 let f = build();
38 let mut h = SubMsPerfHarness::new("bloom-feature", "rust");
39 let st = h.stage("probe", size);
40 for k in &ks[..size] {
41 st.time(|| probe(&f, k));
42 }
43 summarize(&h)
44 .stages
45 .iter()
46 .find(|s| s.name == "probe")
47 .map_or(0, |s| s.p99_ns)
48}
49
50fn mutate_p99<T>(
52 size: usize,
53 mut make: impl FnMut() -> T,
54 mut op: impl FnMut(&mut T, &str),
55 ks: &[String],
56) -> u64 {
57 let mut f = make();
58 let mut h = SubMsPerfHarness::new("bloom-feature", "rust");
59 let st = h.stage("op", size);
60 for k in &ks[..size] {
61 st.time(|| op(&mut f, k));
62 }
63 summarize(&h)
64 .stages
65 .iter()
66 .find(|s| s.name == "op")
67 .map_or(0, |s| s.p99_ns)
68}
69
70fn main() -> io::Result<()> {
71 let ks = keys();
72 let canon = SIZES[SIZES.len() - 1];
73
74 let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
75 .join("..")
76 .join(".subms")
77 .join("features")
78 .join("rust.json");
79 let existing = std::fs::read_to_string(&path).unwrap_or_default();
80 let mut manifest = SubMsFeatureManifest::load_str("rust", &existing);
81 let (source, instance) = SubMsP99Source::from_env();
85 manifest.set_p99_source(source, instance.as_deref());
86
87 #[cfg(feature = "counting")]
89 {
90 use subms_bloom_filter::CountingBloomFilter;
91 let fill = |n: usize| {
92 let mut c = CountingBloomFilter::new(n);
93 for k in &ks[..n] {
94 c.add(k);
95 }
96 c
97 };
98 let sweep: Vec<(usize, u64)> = SIZES
99 .iter()
100 .map(|&n| {
101 (
102 n,
103 probe_p99(n, || fill(n), |c, k| _ = c.might_contain(k), &ks),
104 )
105 })
106 .collect();
107 let (cat, reason) = classify_feature(&sweep, None, None);
108 let mut p99 = BTreeMap::new();
109 p99.insert("contains".to_string(), sweep.last().unwrap().1);
110 p99.insert(
111 "add".to_string(),
112 mutate_p99(
113 canon,
114 || CountingBloomFilter::new(canon),
115 |c, k| c.add(k),
116 &ks,
117 ),
118 );
119 p99.insert(
120 "remove".to_string(),
121 mutate_p99(canon, || fill(canon), |c, k| c.remove(k), &ks),
122 );
123 manifest.set_feature("counting", cat, &p99, &reason);
124 }
125
126 #[cfg(feature = "scalable")]
128 {
129 use subms_bloom_filter::ScalableBloomFilter;
130 let fill = |n: usize| {
131 let mut s = ScalableBloomFilter::new(1_000);
132 for k in &ks[..n] {
133 s.add(k);
134 }
135 s
136 };
137 let sweep: Vec<(usize, u64)> = SIZES
138 .iter()
139 .map(|&n| {
140 (
141 n,
142 probe_p99(n, || fill(n), |s, k| _ = s.might_contain(k), &ks),
143 )
144 })
145 .collect();
146 let (cat, reason) = classify_feature(&sweep, None, None);
147 let mut p99 = BTreeMap::new();
148 p99.insert("contains".to_string(), sweep.last().unwrap().1);
149 p99.insert(
150 "add".to_string(),
151 mutate_p99(
152 canon,
153 || ScalableBloomFilter::new(1_000),
154 |s, k| s.add(k),
155 &ks,
156 ),
157 );
158 manifest.set_feature("scalable", cat, &p99, &reason);
159 }
160
161 #[cfg(feature = "partitioned")]
163 {
164 use subms_bloom_filter::PartitionedBloomFilter;
165 let fill = |n: usize| {
166 let mut p = PartitionedBloomFilter::new(n);
167 for k in &ks[..n] {
168 p.add(k);
169 }
170 p
171 };
172 let sweep: Vec<(usize, u64)> = SIZES
173 .iter()
174 .map(|&n| {
175 (
176 n,
177 probe_p99(n, || fill(n), |p, k| _ = p.might_contain(k), &ks),
178 )
179 })
180 .collect();
181 let (cat, reason) = classify_feature(&sweep, None, None);
182 let mut p99 = BTreeMap::new();
183 p99.insert("contains".to_string(), sweep.last().unwrap().1);
184 p99.insert(
185 "add".to_string(),
186 mutate_p99(
187 canon,
188 || PartitionedBloomFilter::new(canon),
189 |p, k| p.add(k),
190 &ks,
191 ),
192 );
193 manifest.set_feature("partitioned", cat, &p99, &reason);
194 }
195
196 #[cfg(feature = "serde")]
198 {
199 let (cat, reason) = classify_feature(&[], None, None);
200 manifest.set_feature("serde", cat, &BTreeMap::new(), &reason);
201 }
202
203 std::fs::create_dir_all(path.parent().unwrap())?;
204 std::fs::write(&path, manifest.to_json())?;
205 io::stdout().write_all(manifest.to_json().as_bytes())?;
206 Ok(())
207}