pub struct GrowableBump { /* private fields */ }Expand description
Multi-chunk bump-pointer arena that auto-grows on exhaustion.
Implementations§
Source§impl GrowableBump
impl GrowableBump
Sourcepub fn new() -> Self
pub fn new() -> Self
New arena with a 4 KiB initial chunk.
Examples found in repository?
examples/perf_features.rs (line 108)
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 // Stamp the box these numbers came from. The bench runs wherever it is
63 // invoked, so an unstamped manifest is indistinguishable from a fleet
64 // capture; the renderer will not publish one it cannot attribute.
65 let (source, instance) = SubMsP99Source::from_env();
66 manifest.set_p99_source(source, instance.as_deref());
67
68 // ---------- typed: one Copy type, slot handles, reuse on free ----------
69 #[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 // Every timed op here takes the slot the previous one freed, so the
89 // reuse path is what is measured rather than the append path.
90 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 // ---------- growable: a new chunk when the active one runs out ----------
102 #[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 // ---------- stats: live counters on the alloc path ----------
133 #[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 // ---------- aligned: explicit per-allocation alignment ----------
162 #[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}Sourcepub fn with_capacity(initial: usize) -> Self
pub fn with_capacity(initial: usize) -> Self
New arena with the requested initial chunk size (64-byte floor, 16-byte aligned chunk allocation).
Examples found in repository?
examples/sample_app.rs (line 140)
137fn growable_deep_book() {
138 use subms_arena_allocator::GrowableBump;
139 println!("\n== growable: a deep-book tick that outgrows the chunk ==");
140 let mut scratch = GrowableBump::with_capacity(256);
141 for i in 0..200u64 {
142 scratch.alloc_copy(Level {
143 price_ticks: 10_000 + i,
144 qty: 1,
145 });
146 }
147 let grown = scratch.chunk_count();
148 println!(
149 " 200 levels -> {grown} chunks, {}B retained",
150 scratch.total_capacity()
151 );
152 assert!(grown > 1, "deep book forced a grow");
153 scratch.reset();
154 assert_eq!(
155 scratch.chunk_count(),
156 1,
157 "reset keeps only the largest chunk"
158 );
159 let cap_after = scratch.total_capacity();
160 for i in 0..50u64 {
161 scratch.alloc_copy(Level {
162 price_ticks: 10_000 + i,
163 qty: 1,
164 });
165 }
166 assert_eq!(
167 scratch.total_capacity(),
168 cap_after,
169 "steady-state tick is grow-free"
170 );
171}Sourcepub fn alloc_copy<T: Copy>(&mut self, value: T) -> &mut T
pub fn alloc_copy<T: Copy>(&mut self, value: T) -> &mut T
Allocate a Copy value. Grows on exhaustion.
Examples found in repository?
examples/sample_app.rs (lines 142-145)
137fn growable_deep_book() {
138 use subms_arena_allocator::GrowableBump;
139 println!("\n== growable: a deep-book tick that outgrows the chunk ==");
140 let mut scratch = GrowableBump::with_capacity(256);
141 for i in 0..200u64 {
142 scratch.alloc_copy(Level {
143 price_ticks: 10_000 + i,
144 qty: 1,
145 });
146 }
147 let grown = scratch.chunk_count();
148 println!(
149 " 200 levels -> {grown} chunks, {}B retained",
150 scratch.total_capacity()
151 );
152 assert!(grown > 1, "deep book forced a grow");
153 scratch.reset();
154 assert_eq!(
155 scratch.chunk_count(),
156 1,
157 "reset keeps only the largest chunk"
158 );
159 let cap_after = scratch.total_capacity();
160 for i in 0..50u64 {
161 scratch.alloc_copy(Level {
162 price_ticks: 10_000 + i,
163 qty: 1,
164 });
165 }
166 assert_eq!(
167 scratch.total_capacity(),
168 cap_after,
169 "steady-state tick is grow-free"
170 );
171}More examples
examples/perf_features.rs (line 110)
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 // Stamp the box these numbers came from. The bench runs wherever it is
63 // invoked, so an unstamped manifest is indistinguishable from a fleet
64 // capture; the renderer will not publish one it cannot attribute.
65 let (source, instance) = SubMsP99Source::from_env();
66 manifest.set_p99_source(source, instance.as_deref());
67
68 // ---------- typed: one Copy type, slot handles, reuse on free ----------
69 #[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 // Every timed op here takes the slot the previous one freed, so the
89 // reuse path is what is measured rather than the append path.
90 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 // ---------- growable: a new chunk when the active one runs out ----------
102 #[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 // ---------- stats: live counters on the alloc path ----------
133 #[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 // ---------- aligned: explicit per-allocation alignment ----------
162 #[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}Sourcepub fn alloc_raw(&mut self, layout: Layout) -> *mut u8
pub fn alloc_raw(&mut self, layout: Layout) -> *mut u8
Allocate layout.size() bytes aligned to layout.align().
Grows on exhaustion.
Sourcepub fn reset(&mut self)
pub fn reset(&mut self)
Rewind every chunk. Keeps only the largest chunk; smaller chunks are dropped. Subsequent allocations reuse the kept chunk without further grow events for workloads within its size.
Examples found in repository?
examples/sample_app.rs (line 153)
137fn growable_deep_book() {
138 use subms_arena_allocator::GrowableBump;
139 println!("\n== growable: a deep-book tick that outgrows the chunk ==");
140 let mut scratch = GrowableBump::with_capacity(256);
141 for i in 0..200u64 {
142 scratch.alloc_copy(Level {
143 price_ticks: 10_000 + i,
144 qty: 1,
145 });
146 }
147 let grown = scratch.chunk_count();
148 println!(
149 " 200 levels -> {grown} chunks, {}B retained",
150 scratch.total_capacity()
151 );
152 assert!(grown > 1, "deep book forced a grow");
153 scratch.reset();
154 assert_eq!(
155 scratch.chunk_count(),
156 1,
157 "reset keeps only the largest chunk"
158 );
159 let cap_after = scratch.total_capacity();
160 for i in 0..50u64 {
161 scratch.alloc_copy(Level {
162 price_ticks: 10_000 + i,
163 qty: 1,
164 });
165 }
166 assert_eq!(
167 scratch.total_capacity(),
168 cap_after,
169 "steady-state tick is grow-free"
170 );
171}More examples
examples/perf_features.rs (line 125)
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 // Stamp the box these numbers came from. The bench runs wherever it is
63 // invoked, so an unstamped manifest is indistinguishable from a fleet
64 // capture; the renderer will not publish one it cannot attribute.
65 let (source, instance) = SubMsP99Source::from_env();
66 manifest.set_p99_source(source, instance.as_deref());
67
68 // ---------- typed: one Copy type, slot handles, reuse on free ----------
69 #[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 // Every timed op here takes the slot the previous one freed, so the
89 // reuse path is what is measured rather than the append path.
90 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 // ---------- growable: a new chunk when the active one runs out ----------
102 #[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 // ---------- stats: live counters on the alloc path ----------
133 #[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 // ---------- aligned: explicit per-allocation alignment ----------
162 #[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}Sourcepub fn total_capacity(&self) -> usize
pub fn total_capacity(&self) -> usize
Bytes allocated across every retained chunk.
Examples found in repository?
examples/sample_app.rs (line 150)
137fn growable_deep_book() {
138 use subms_arena_allocator::GrowableBump;
139 println!("\n== growable: a deep-book tick that outgrows the chunk ==");
140 let mut scratch = GrowableBump::with_capacity(256);
141 for i in 0..200u64 {
142 scratch.alloc_copy(Level {
143 price_ticks: 10_000 + i,
144 qty: 1,
145 });
146 }
147 let grown = scratch.chunk_count();
148 println!(
149 " 200 levels -> {grown} chunks, {}B retained",
150 scratch.total_capacity()
151 );
152 assert!(grown > 1, "deep book forced a grow");
153 scratch.reset();
154 assert_eq!(
155 scratch.chunk_count(),
156 1,
157 "reset keeps only the largest chunk"
158 );
159 let cap_after = scratch.total_capacity();
160 for i in 0..50u64 {
161 scratch.alloc_copy(Level {
162 price_ticks: 10_000 + i,
163 qty: 1,
164 });
165 }
166 assert_eq!(
167 scratch.total_capacity(),
168 cap_after,
169 "steady-state tick is grow-free"
170 );
171}Sourcepub fn chunk_count(&self) -> usize
pub fn chunk_count(&self) -> usize
Number of chunks currently retained.
Examples found in repository?
examples/sample_app.rs (line 147)
137fn growable_deep_book() {
138 use subms_arena_allocator::GrowableBump;
139 println!("\n== growable: a deep-book tick that outgrows the chunk ==");
140 let mut scratch = GrowableBump::with_capacity(256);
141 for i in 0..200u64 {
142 scratch.alloc_copy(Level {
143 price_ticks: 10_000 + i,
144 qty: 1,
145 });
146 }
147 let grown = scratch.chunk_count();
148 println!(
149 " 200 levels -> {grown} chunks, {}B retained",
150 scratch.total_capacity()
151 );
152 assert!(grown > 1, "deep book forced a grow");
153 scratch.reset();
154 assert_eq!(
155 scratch.chunk_count(),
156 1,
157 "reset keeps only the largest chunk"
158 );
159 let cap_after = scratch.total_capacity();
160 for i in 0..50u64 {
161 scratch.alloc_copy(Level {
162 price_ticks: 10_000 + i,
163 qty: 1,
164 });
165 }
166 assert_eq!(
167 scratch.total_capacity(),
168 cap_after,
169 "steady-state tick is grow-free"
170 );
171}Trait Implementations§
Auto Trait Implementations§
impl !Send for GrowableBump
impl !Sync for GrowableBump
impl Freeze for GrowableBump
impl RefUnwindSafe for GrowableBump
impl Unpin for GrowableBump
impl UnsafeUnpin for GrowableBump
impl UnwindSafe for GrowableBump
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more