phantom_protocol/transport/
buffer_pool.rs1use crossbeam_queue::ArrayQueue;
14use std::cell::RefCell;
15use std::sync::atomic::{AtomicUsize, Ordering};
16
17const BATCH_SIZE: usize = 32;
18const MAX_LOCAL_BUFFERS: usize = 64;
19
20pub struct BufferPool {
22 buffers: ArrayQueue<Vec<u8>>,
24 buffer_size: usize,
26 #[allow(dead_code)]
28 max_buffers: usize,
29 allocations: AtomicUsize,
31 hits: AtomicUsize,
33}
34
35thread_local! {
36 static LOCAL_POOL: RefCell<Vec<Vec<u8>>> = RefCell::new(Vec::with_capacity(MAX_LOCAL_BUFFERS));
37}
38
39impl BufferPool {
40 pub fn new(buffer_size: usize, initial_count: usize, max_buffers: usize) -> Self {
42 let buffers = ArrayQueue::new(max_buffers);
43 let count = std::cmp::min(initial_count, max_buffers);
44 for _ in 0..count {
45 let _ = buffers.push(vec![0u8; buffer_size]);
46 }
47
48 Self {
49 buffers,
50 buffer_size,
51 max_buffers,
52 allocations: AtomicUsize::new(count),
53 hits: AtomicUsize::new(0),
54 }
55 }
56
57 #[inline]
59 pub fn acquire(&self) -> PooledBuffer<'_> {
60 let mut buffer: Option<Vec<u8>> = LOCAL_POOL.with(|local| {
61 let mut local_pool = local.borrow_mut();
62 if let Some(mut buf) = local_pool.pop() {
63 buf.clear();
64 return Some(buf);
65 }
66 None
67 });
68
69 if buffer.is_none() {
70 LOCAL_POOL.with(|local| {
72 let mut local_pool = local.borrow_mut();
73 for _ in 0..BATCH_SIZE {
74 if let Some(mut buf) = self.buffers.pop() {
75 buf.clear();
76 local_pool.push(buf);
77 } else {
78 break;
79 }
80 }
81 });
82
83 buffer = LOCAL_POOL.with(|local| {
84 let mut local_pool = local.borrow_mut();
85 local_pool.pop()
86 });
87 }
88
89 let buffer = if let Some(buf) = buffer {
90 self.hits.fetch_add(1, Ordering::Relaxed);
91 buf
92 } else {
93 self.allocations.fetch_add(1, Ordering::Relaxed);
94 Vec::with_capacity(self.buffer_size)
95 };
96
97 PooledBuffer { buffer, pool: self }
98 }
99
100 #[inline]
102 fn return_buffer(&self, mut buffer: Vec<u8>) {
103 buffer.clear();
104 LOCAL_POOL.with(|local| {
105 let mut local_pool = local.borrow_mut();
106 if local_pool.len() < MAX_LOCAL_BUFFERS {
107 local_pool.push(buffer);
108 } else {
109 let half = MAX_LOCAL_BUFFERS / 2;
111 for _ in 0..half {
112 if let Some(buf) = local_pool.pop() {
113 let _ = self.buffers.push(buf);
114 }
115 }
116
117 local_pool.push(buffer);
118 }
119 });
120 }
121
122 pub fn stats(&self) -> PoolStats {
124 PoolStats {
125 allocations: self.allocations.load(Ordering::Relaxed),
126 hits: self.hits.load(Ordering::Relaxed),
127 pool_size: self.buffers.len(),
128 }
129 }
130}
131
132pub struct PooledBuffer<'a> {
134 buffer: Vec<u8>,
135 pool: &'a BufferPool,
136}
137
138impl<'a> PooledBuffer<'a> {
139 #[allow(clippy::should_implement_trait)]
145 #[inline]
146 pub fn as_mut(&mut self) -> &mut Vec<u8> {
147 &mut self.buffer
148 }
149
150 #[allow(clippy::should_implement_trait)]
156 #[inline]
157 pub fn as_ref(&self) -> &[u8] {
158 &self.buffer
159 }
160
161 #[inline]
163 pub fn len(&self) -> usize {
164 self.buffer.len()
165 }
166
167 #[inline]
169 pub fn is_empty(&self) -> bool {
170 self.buffer.is_empty()
171 }
172}
173
174impl<'a> std::ops::Deref for PooledBuffer<'a> {
175 type Target = Vec<u8>;
176
177 #[inline]
178 fn deref(&self) -> &Self::Target {
179 &self.buffer
180 }
181}
182
183impl<'a> std::ops::DerefMut for PooledBuffer<'a> {
184 #[inline]
185 fn deref_mut(&mut self) -> &mut Self::Target {
186 &mut self.buffer
187 }
188}
189
190impl<'a> Drop for PooledBuffer<'a> {
191 fn drop(&mut self) {
192 let buffer = std::mem::take(&mut self.buffer);
193 if buffer.capacity() > 0 {
195 self.pool.return_buffer(buffer);
196 }
197 }
198}
199
200#[derive(Debug, Clone, Copy)]
202pub struct PoolStats {
203 pub allocations: usize,
204 pub hits: usize,
205 pub pool_size: usize,
206}
207
208impl PoolStats {
209 pub fn hit_rate(&self) -> f64 {
211 if self.allocations + self.hits == 0 {
212 0.0
213 } else {
214 self.hits as f64 / (self.allocations + self.hits) as f64
215 }
216 }
217}
218
219static GLOBAL_POOL: std::sync::OnceLock<BufferPool> = std::sync::OnceLock::new();
221
222pub fn global_pool() -> &'static BufferPool {
224 GLOBAL_POOL.get_or_init(|| {
225 BufferPool::new(64 * 1024, 1024, 65536)
227 })
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233 use std::thread;
234
235 #[test]
236 fn test_buffer_pool() {
237 let pool = BufferPool::new(1024, 4, 16);
238
239 let mut buf1 = pool.acquire();
240 buf1.extend_from_slice(b"hello");
241 assert_eq!(buf1.len(), 5);
242
243 let buf2 = pool.acquire();
244 assert_eq!(buf2.len(), 0);
245
246 drop(buf1);
247 drop(buf2);
248
249 LOCAL_POOL.with(|local| {
252 assert_eq!(local.borrow().len(), 4);
253 });
254 }
255
256 #[test]
257 fn test_thread_local_flushing() {
258 let pool = std::sync::Arc::new(BufferPool::new(1024, 0, 100));
259
260 let p_clone = pool.clone();
261 thread::spawn(move || {
262 let mut bufs = Vec::new();
263 for _ in 0..70 {
265 bufs.push(p_clone.acquire());
266 }
267
268 drop(bufs);
270
271 let mut count = 0;
273 LOCAL_POOL.with(|local| {
274 count = local.borrow().len();
275 });
276 assert!(count <= MAX_LOCAL_BUFFERS);
277 })
278 .join()
279 .unwrap();
280
281 assert!(pool.buffers.len() > 0);
283 }
284}