Skip to content

Commit b484041

Browse files
authored
[runtime/iobuf/pool] pooled buffers share Layout (#3752)
1 parent 72a6b16 commit b484041

5 files changed

Lines changed: 813 additions & 413 deletions

File tree

runtime/src/iobuf/benches/freelist.rs

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,16 @@
1616
//! steady-state shape of multi-threaded freelist reuse.
1717
//!
1818
//! The benchmarked entries are synthetic slot ids paired with a small
19-
//! [`AlignedBuffer`]. That keeps the shape close to the real pooled freelist
19+
//! [`PooledBuffer`]. That keeps the shape close to the real pooled freelist
2020
//! while avoiding unrelated `BufferPool` logic.
2121
2222
use super::utils::{measure, Threading};
23-
use commonware_runtime::iobuf::bench::{AlignedBuffer, Freelist};
23+
use commonware_runtime::iobuf::bench::{Freelist, PooledBuffer};
2424
use commonware_utils::sync::Mutex;
2525
use criterion::Criterion;
2626
use crossbeam_queue::ArrayQueue;
2727
use std::{
28+
alloc::Layout,
2829
hint::black_box,
2930
num::{NonZeroU32, NonZeroUsize},
3031
sync::Arc,
@@ -35,16 +36,26 @@ const BATCH_SIZES: &[usize] = &[1, 2, 4, 8, 16, 32];
3536

3637
const BENCH_BUFFER_CAPACITY: usize = 256;
3738
const BENCH_BUFFER_ALIGNMENT: usize = 64;
39+
const BENCH_LAYOUT: Layout =
40+
match Layout::from_size_align(BENCH_BUFFER_CAPACITY, BENCH_BUFFER_ALIGNMENT) {
41+
Ok(layout) => layout,
42+
Err(_) => panic!("valid bench layout"),
43+
};
3844

3945
#[derive(Debug)]
4046
struct Entry {
4147
slot: u32,
42-
buffer: AlignedBuffer,
48+
buffer: PooledBuffer,
4349
}
4450

45-
// SAFETY: this entry uniquely owns its buffer and slot id, so moving it across
46-
// threads is equivalent to moving a uniquely-owned heap allocation.
47-
unsafe impl Send for Entry {}
51+
impl Entry {
52+
fn new(slot: usize) -> Self {
53+
Self {
54+
slot: slot as u32,
55+
buffer: PooledBuffer::new(BENCH_LAYOUT),
56+
}
57+
}
58+
}
4859

4960
trait FreelistImplementation: Send + Sync {
5061
fn as_str() -> &'static str;
@@ -84,6 +95,12 @@ impl<S: FreelistImplementation> WorkerState<S> {
8495
}
8596
}
8697

98+
impl<S: FreelistImplementation> Drop for WorkerState<S> {
99+
fn drop(&mut self) {
100+
self.shared.put_batch(&mut self.held);
101+
}
102+
}
103+
87104
pub fn bench(c: &mut Criterion) {
88105
let threadings = Threading::standard();
89106

@@ -147,12 +164,7 @@ impl FreelistImplementation for MutexVec {
147164
}
148165

149166
fn with_capacity(capacity: usize, _parallelism: usize) -> Self {
150-
let slots = (0..capacity)
151-
.map(|slot| Entry {
152-
slot: slot as u32,
153-
buffer: AlignedBuffer::new(BENCH_BUFFER_CAPACITY, BENCH_BUFFER_ALIGNMENT),
154-
})
155-
.collect();
167+
let slots = (0..capacity).map(Entry::new).collect();
156168
Self(Mutex::new(slots))
157169
}
158170

@@ -171,6 +183,15 @@ impl FreelistImplementation for MutexVec {
171183
}
172184
}
173185

186+
impl Drop for MutexVec {
187+
fn drop(&mut self) {
188+
for entry in self.0.get_mut().drain(..) {
189+
// SAFETY: benchmark entries are allocated with `BENCH_LAYOUT`.
190+
unsafe { entry.buffer.deallocate(BENCH_LAYOUT) };
191+
}
192+
}
193+
}
194+
174195
struct ArrayQueueFreelist(ArrayQueue<Entry>);
175196

176197
impl FreelistImplementation for ArrayQueueFreelist {
@@ -182,10 +203,7 @@ impl FreelistImplementation for ArrayQueueFreelist {
182203
let queue = ArrayQueue::new(capacity);
183204
for slot in 0..capacity {
184205
queue
185-
.push(Entry {
186-
slot: slot as u32,
187-
buffer: AlignedBuffer::new(BENCH_BUFFER_CAPACITY, BENCH_BUFFER_ALIGNMENT),
188-
})
206+
.push(Entry::new(slot))
189207
.expect("array queue prefill must fit");
190208
}
191209
Self(queue)
@@ -212,24 +230,28 @@ impl FreelistImplementation for ArrayQueueFreelist {
212230
}
213231
}
214232

233+
impl Drop for ArrayQueueFreelist {
234+
fn drop(&mut self) {
235+
while let Some(entry) = self.0.pop() {
236+
// SAFETY: benchmark entries are allocated with `BENCH_LAYOUT`.
237+
unsafe { entry.buffer.deallocate(BENCH_LAYOUT) };
238+
}
239+
}
240+
}
241+
215242
impl FreelistImplementation for Freelist {
216243
fn as_str() -> &'static str {
217244
"freelist"
218245
}
219246

220247
fn with_capacity(capacity: usize, parallelism: usize) -> Self {
221-
let freelist = Self::new(
248+
Self::new(
222249
NonZeroU32::new(u32::try_from(capacity).expect("bench capacity must fit in u32"))
223250
.expect("bench capacity must be non-zero"),
224251
NonZeroUsize::new(parallelism).expect("bench parallelism must be non-zero"),
225-
);
226-
for slot in 0..capacity {
227-
freelist.put(
228-
slot as u32,
229-
AlignedBuffer::new(BENCH_BUFFER_CAPACITY, BENCH_BUFFER_ALIGNMENT),
230-
);
231-
}
232-
freelist
252+
BENCH_LAYOUT,
253+
true,
254+
)
233255
}
234256

235257
#[inline]

0 commit comments

Comments
 (0)