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
2222use super :: utils:: { measure, Threading } ;
23- use commonware_runtime:: iobuf:: bench:: { AlignedBuffer , Freelist } ;
23+ use commonware_runtime:: iobuf:: bench:: { Freelist , PooledBuffer } ;
2424use commonware_utils:: sync:: Mutex ;
2525use criterion:: Criterion ;
2626use crossbeam_queue:: ArrayQueue ;
2727use 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
3637const BENCH_BUFFER_CAPACITY : usize = 256 ;
3738const 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 ) ]
4046struct 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
4960trait 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+
87104pub 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+
174195struct ArrayQueueFreelist ( ArrayQueue < Entry > ) ;
175196
176197impl 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+
215242impl 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