perf_features/
perf_features.rs1use std::collections::BTreeMap;
24use std::io::{self, Write};
25use std::path::PathBuf;
26
27use subms::{SubMsFeatureManifest, SubMsP99Source, SubMsPerfHarness, classify_feature, summarize};
28
29const SIZES: [usize; 3] = [4_096, 32_768, 262_144];
31
32fn stage_stats(h: &SubMsPerfHarness, name: &str) -> (u64, u64) {
33 summarize(h)
34 .stages
35 .iter()
36 .find(|s| s.name == name)
37 .map_or((0, 0), |s| (s.p50_ns, s.p99_ns))
38}
39
40fn run_p50_p99(n: usize, mut op: impl FnMut(usize)) -> (u64, u64) {
42 let mut h = SubMsPerfHarness::new("arena-feature", "rust");
43 {
44 let st = h.stage("op", n);
45 for i in 0..n {
46 st.time(|| op(i));
47 }
48 }
49 stage_stats(&h, "op")
50}
51
52fn main() -> io::Result<()> {
53 let canon = SIZES[SIZES.len() - 1];
54
55 let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
56 .join("..")
57 .join(".subms")
58 .join("features")
59 .join("rust.json");
60 let existing = std::fs::read_to_string(&path).unwrap_or_default();
61 let mut manifest = SubMsFeatureManifest::load_str("rust", &existing);
62 let (source, instance) = SubMsP99Source::from_env();
66 manifest.set_p99_source(source, instance.as_deref());
67
68 #[cfg(feature = "typed")]
70 {
71 use subms_arena_allocator::TypedArena;
72 let sweep: Vec<(usize, u64)> = SIZES
73 .iter()
74 .map(|&n| {
75 let mut arena: TypedArena<u64> = TypedArena::with_capacity(n);
76 let (p50, _) = run_p50_p99(n, |i| {
77 std::hint::black_box(arena.alloc(i as u64));
78 });
79 (n, p50)
80 })
81 .collect();
82 let (cat, reason) = classify_feature(&sweep, None, None);
83
84 let mut arena: TypedArena<u64> = TypedArena::with_capacity(canon);
85 let (_, alloc99) = run_p50_p99(canon, |i| {
86 std::hint::black_box(arena.alloc(i as u64));
87 });
88 let mut churn: TypedArena<u64> = TypedArena::with_capacity(2);
91 let (_, free99) = run_p50_p99(canon, |i| {
92 let slot = churn.alloc(i as u64);
93 churn.free(slot);
94 });
95 let mut p99 = BTreeMap::new();
96 p99.insert("alloc".to_string(), alloc99);
97 p99.insert("free".to_string(), free99);
98 manifest.set_feature("typed", cat, &p99, &reason);
99 }
100
101 #[cfg(feature = "growable")]
103 {
104 use subms_arena_allocator::GrowableBump;
105 let sweep: Vec<(usize, u64)> = SIZES
106 .iter()
107 .map(|&n| {
108 let mut a = GrowableBump::new();
109 let (p50, _) = run_p50_p99(n, |i| {
110 std::hint::black_box(a.alloc_copy(i as u64));
111 });
112 (n, p50)
113 })
114 .collect();
115 let (cat, reason) = classify_feature(&sweep, None, None);
116
117 let mut a = GrowableBump::new();
118 let (_, alloc99) = run_p50_p99(canon, |i| {
119 std::hint::black_box(a.alloc_copy(i as u64));
120 });
121 let mut filled = GrowableBump::new();
122 for i in 0..canon {
123 filled.alloc_copy(i as u64);
124 }
125 let (_, reset99) = run_p50_p99(1, |_| filled.reset());
126 let mut p99 = BTreeMap::new();
127 p99.insert("alloc".to_string(), alloc99);
128 p99.insert("reset".to_string(), reset99);
129 manifest.set_feature("growable", cat, &p99, &reason);
130 }
131
132 #[cfg(feature = "stats")]
134 {
135 use subms_arena_allocator::StatsBump;
136 let sweep: Vec<(usize, u64)> = SIZES
137 .iter()
138 .map(|&n| {
139 let mut a = StatsBump::new();
140 let (p50, _) = run_p50_p99(n, |i| {
141 std::hint::black_box(a.alloc_copy(i as u64));
142 });
143 (n, p50)
144 })
145 .collect();
146 let (cat, reason) = classify_feature(&sweep, None, None);
147
148 let mut a = StatsBump::new();
149 let (_, alloc99) = run_p50_p99(canon, |i| {
150 std::hint::black_box(a.alloc_copy(i as u64));
151 });
152 let (_, snap99) = run_p50_p99(canon, |_| {
153 std::hint::black_box(a.stats());
154 });
155 let mut p99 = BTreeMap::new();
156 p99.insert("alloc".to_string(), alloc99);
157 p99.insert("stats".to_string(), snap99);
158 manifest.set_feature("stats", cat, &p99, &reason);
159 }
160
161 #[cfg(feature = "aligned")]
163 {
164 use subms_arena_allocator::AlignedBump;
165 let sweep: Vec<(usize, u64)> = SIZES
166 .iter()
167 .map(|&n| {
168 let mut a = AlignedBump::with_capacity(n * 16 + 4096);
169 let (p50, _) = run_p50_p99(n, |_| {
170 std::hint::black_box(a.alloc_aligned(8, 8).len());
171 });
172 (n, p50)
173 })
174 .collect();
175 let (cat, reason) = classify_feature(&sweep, None, None);
176
177 let mut a = AlignedBump::with_capacity(canon * 16 + 4096);
178 let (_, alloc99) = run_p50_p99(canon, |_| {
179 std::hint::black_box(a.alloc_aligned(8, 8).len());
180 });
181 let mut p99 = BTreeMap::new();
182 p99.insert("alloc_aligned".to_string(), alloc99);
183 manifest.set_feature("aligned", cat, &p99, &reason);
184 }
185
186 std::fs::create_dir_all(path.parent().unwrap())?;
187 std::fs::write(&path, manifest.to_json())?;
188 io::stdout().write_all(manifest.to_json().as_bytes())?;
189 Ok(())
190}