1use crate::algebra::{Binding, Solution, Term, Variable};
7use std::collections::{HashMap, VecDeque};
8use std::sync::{Arc, Mutex};
9
10pub struct BufferPoolManager {
12 solution_pool: Arc<Mutex<BufferPool<Solution>>>,
13 binding_pool: Arc<Mutex<BufferPool<Binding>>>,
14 hashmap_pool: Arc<Mutex<BufferPool<HashMap<Variable, Term>>>>,
15 vector_pool: Arc<Mutex<BufferPool<Vec<Binding>>>>,
16 stats: Arc<Mutex<BufferPoolStats>>,
17}
18
19impl BufferPoolManager {
20 pub fn new() -> Self {
22 Self::with_capacities(1000, 2000, 500, 1000)
23 }
24
25 pub fn with_capacities(
27 solution_capacity: usize,
28 binding_capacity: usize,
29 hashmap_capacity: usize,
30 vector_capacity: usize,
31 ) -> Self {
32 Self {
33 solution_pool: Arc::new(Mutex::new(BufferPool::new_for_solutions(solution_capacity))),
34 binding_pool: Arc::new(Mutex::new(BufferPool::new_for_bindings(binding_capacity))),
35 hashmap_pool: Arc::new(Mutex::new(BufferPool::new_for_hashmaps(hashmap_capacity))),
36 vector_pool: Arc::new(Mutex::new(BufferPool::new_for_vectors(vector_capacity))),
37 stats: Arc::new(Mutex::new(BufferPoolStats::default())),
38 }
39 }
40
41 pub fn acquire_solution(&self) -> PooledSolution<'_> {
43 let solution = self.solution_pool.lock().expect("lock poisoned").acquire();
44 self.update_stats("solution", true);
45
46 PooledSolution {
47 solution: Some(solution),
48 pool: Arc::clone(&self.solution_pool),
49 manager: Some(self),
50 }
51 }
52
53 pub fn acquire_binding(&self) -> PooledBinding<'_> {
55 let binding = self.binding_pool.lock().expect("lock poisoned").acquire();
56 self.update_stats("binding", true);
57
58 PooledBinding {
59 binding: Some(binding),
60 pool: Arc::clone(&self.binding_pool),
61 manager: Some(self),
62 }
63 }
64
65 pub fn acquire_hashmap(&self) -> PooledHashMap<'_> {
67 let hashmap = self.hashmap_pool.lock().expect("lock poisoned").acquire();
68 self.update_stats("hashmap", true);
69
70 PooledHashMap {
71 hashmap: Some(hashmap),
72 pool: Arc::clone(&self.hashmap_pool),
73 manager: Some(self),
74 }
75 }
76
77 pub fn acquire_vector(&self) -> PooledVector<'_> {
79 let vector = self.vector_pool.lock().expect("lock poisoned").acquire();
80 self.update_stats("vector", true);
81
82 PooledVector {
83 vector: Some(vector),
84 pool: Arc::clone(&self.vector_pool),
85 manager: Some(self),
86 }
87 }
88
89 fn update_stats(&self, pool_type: &str, is_acquire: bool) {
91 let mut stats = self.stats.lock().expect("lock poisoned");
92
93 let pool_stats = match pool_type {
94 "solution" => &mut stats.solution_stats,
95 "binding" => &mut stats.binding_stats,
96 "hashmap" => &mut stats.hashmap_stats,
97 "vector" => &mut stats.vector_stats,
98 _ => return,
99 };
100
101 if is_acquire {
102 pool_stats.total_acquires += 1;
103 } else {
104 pool_stats.total_returns += 1;
105 }
106 }
107
108 pub fn get_stats(&self) -> BufferPoolStats {
110 self.stats.lock().expect("lock poisoned").clone()
111 }
112
113 pub fn clear_all_pools(&self) {
115 self.solution_pool.lock().expect("lock poisoned").clear();
116 self.binding_pool.lock().expect("lock poisoned").clear();
117 self.hashmap_pool.lock().expect("lock poisoned").clear();
118 self.vector_pool.lock().expect("lock poisoned").clear();
119 }
120
121 pub fn estimate_total_memory_usage(&self) -> usize {
123 let solution_mem = self
124 .solution_pool
125 .lock()
126 .expect("lock poisoned")
127 .estimate_memory_usage()
128 * std::mem::size_of::<Solution>();
129 let binding_mem = self
130 .binding_pool
131 .lock()
132 .expect("lock poisoned")
133 .estimate_memory_usage()
134 * std::mem::size_of::<Binding>();
135 let hashmap_mem = self
136 .hashmap_pool
137 .lock()
138 .expect("lock poisoned")
139 .estimate_memory_usage()
140 * std::mem::size_of::<HashMap<Variable, Term>>();
141 let vector_mem = self
142 .vector_pool
143 .lock()
144 .expect("lock poisoned")
145 .estimate_memory_usage()
146 * std::mem::size_of::<Vec<Binding>>();
147
148 solution_mem + binding_mem + hashmap_mem + vector_mem
149 }
150}
151
152impl Default for BufferPoolManager {
153 fn default() -> Self {
154 Self::new()
155 }
156}
157
158struct BufferPool<T> {
160 available: VecDeque<T>,
161 max_capacity: usize,
162 factory: fn() -> T,
163}
164
165impl<T> BufferPool<T> {
166 #[allow(dead_code)]
167 fn new(max_capacity: usize) -> Self
168 where
169 T: Default,
170 {
171 Self {
172 available: VecDeque::new(),
173 max_capacity,
174 factory: T::default,
175 }
176 }
177
178 fn acquire(&mut self) -> T {
179 self.available
180 .pop_front()
181 .unwrap_or_else(|| (self.factory)())
182 }
183
184 #[allow(dead_code)]
185 fn return_object(&mut self, mut obj: T) {
186 if self.available.len() < self.max_capacity {
187 self.reset_object(&mut obj);
189 self.available.push_back(obj);
190 }
191 }
193
194 fn clear(&mut self) {
195 self.available.clear();
196 }
197
198 fn estimate_memory_usage(&self) -> usize {
199 self.available.len()
200 }
201
202 #[allow(dead_code)]
203 fn reset_object(&self, _obj: &mut T) {
204 }
207}
208
209impl BufferPool<Solution> {
211 fn new_for_solutions(max_capacity: usize) -> Self {
212 Self {
213 available: VecDeque::new(),
214 max_capacity,
215 factory: Vec::new,
216 }
217 }
218
219 fn return_solution(&mut self, mut solution: Solution) {
220 if self.available.len() < self.max_capacity {
221 solution.clear(); self.available.push_back(solution);
223 }
224 }
225}
226
227impl BufferPool<Binding> {
228 fn new_for_bindings(max_capacity: usize) -> Self {
229 Self {
230 available: VecDeque::new(),
231 max_capacity,
232 factory: HashMap::new,
233 }
234 }
235
236 fn return_binding(&mut self, mut binding: Binding) {
237 if self.available.len() < self.max_capacity {
238 binding.clear(); self.available.push_back(binding);
240 }
241 }
242}
243
244impl BufferPool<HashMap<Variable, Term>> {
245 fn new_for_hashmaps(max_capacity: usize) -> Self {
246 Self {
247 available: VecDeque::new(),
248 max_capacity,
249 factory: HashMap::new,
250 }
251 }
252
253 fn return_hashmap(&mut self, mut hashmap: HashMap<Variable, Term>) {
254 if self.available.len() < self.max_capacity {
255 hashmap.clear(); self.available.push_back(hashmap);
257 }
258 }
259}
260
261impl BufferPool<Vec<Binding>> {
262 fn new_for_vectors(max_capacity: usize) -> Self {
263 Self {
264 available: VecDeque::new(),
265 max_capacity,
266 factory: Vec::new,
267 }
268 }
269
270 fn return_vector(&mut self, mut vector: Vec<Binding>) {
271 if self.available.len() < self.max_capacity {
272 vector.clear(); self.available.push_back(vector);
274 }
275 }
276}
277
278pub struct PooledSolution<'a> {
280 solution: Option<Solution>,
281 pool: Arc<Mutex<BufferPool<Solution>>>,
282 manager: Option<&'a BufferPoolManager>,
283}
284
285impl<'a> PooledSolution<'a> {
286 pub fn get_mut(&mut self) -> &mut Solution {
287 self.solution
288 .as_mut()
289 .expect("pooled solution should exist until drop")
290 }
291
292 pub fn get(&self) -> &Solution {
293 self.solution
294 .as_ref()
295 .expect("pooled solution should exist until drop")
296 }
297}
298
299impl<'a> Drop for PooledSolution<'a> {
300 fn drop(&mut self) {
301 if let Some(solution) = self.solution.take() {
302 self.pool
303 .lock()
304 .expect("lock poisoned")
305 .return_solution(solution);
306 if let Some(manager) = self.manager {
307 manager.update_stats("solution", false);
308 }
309 }
310 }
311}
312
313pub struct PooledBinding<'a> {
315 binding: Option<Binding>,
316 pool: Arc<Mutex<BufferPool<Binding>>>,
317 manager: Option<&'a BufferPoolManager>,
318}
319
320impl<'a> PooledBinding<'a> {
321 pub fn get_mut(&mut self) -> &mut Binding {
322 self.binding
323 .as_mut()
324 .expect("pooled binding should exist until drop")
325 }
326
327 pub fn get(&self) -> &Binding {
328 self.binding
329 .as_ref()
330 .expect("pooled binding should exist until drop")
331 }
332}
333
334impl<'a> Drop for PooledBinding<'a> {
335 fn drop(&mut self) {
336 if let Some(binding) = self.binding.take() {
337 self.pool
338 .lock()
339 .expect("lock poisoned")
340 .return_binding(binding);
341 if let Some(manager) = self.manager {
342 manager.update_stats("binding", false);
343 }
344 }
345 }
346}
347
348pub struct PooledHashMap<'a> {
350 hashmap: Option<HashMap<Variable, Term>>,
351 pool: Arc<Mutex<BufferPool<HashMap<Variable, Term>>>>,
352 manager: Option<&'a BufferPoolManager>,
353}
354
355impl<'a> PooledHashMap<'a> {
356 pub fn get_mut(&mut self) -> &mut HashMap<Variable, Term> {
357 self.hashmap
358 .as_mut()
359 .expect("pooled hashmap should exist until drop")
360 }
361
362 pub fn get(&self) -> &HashMap<Variable, Term> {
363 self.hashmap
364 .as_ref()
365 .expect("pooled hashmap should exist until drop")
366 }
367}
368
369impl<'a> Drop for PooledHashMap<'a> {
370 fn drop(&mut self) {
371 if let Some(hashmap) = self.hashmap.take() {
372 self.pool
373 .lock()
374 .expect("lock poisoned")
375 .return_hashmap(hashmap);
376 if let Some(manager) = self.manager {
377 manager.update_stats("hashmap", false);
378 }
379 }
380 }
381}
382
383pub struct PooledVector<'a> {
385 vector: Option<Vec<Binding>>,
386 pool: Arc<Mutex<BufferPool<Vec<Binding>>>>,
387 manager: Option<&'a BufferPoolManager>,
388}
389
390impl<'a> PooledVector<'a> {
391 pub fn get_mut(&mut self) -> &mut Vec<Binding> {
392 self.vector
393 .as_mut()
394 .expect("pooled vector should exist until drop")
395 }
396
397 pub fn get(&self) -> &Vec<Binding> {
398 self.vector
399 .as_ref()
400 .expect("pooled vector should exist until drop")
401 }
402}
403
404impl<'a> Drop for PooledVector<'a> {
405 fn drop(&mut self) {
406 if let Some(vector) = self.vector.take() {
407 self.pool
408 .lock()
409 .expect("lock poisoned")
410 .return_vector(vector);
411 if let Some(manager) = self.manager {
412 manager.update_stats("vector", false);
413 }
414 }
415 }
416}
417
418#[derive(Debug, Clone, Default)]
420pub struct BufferPoolStats {
421 pub solution_stats: PoolStats,
422 pub binding_stats: PoolStats,
423 pub hashmap_stats: PoolStats,
424 pub vector_stats: PoolStats,
425}
426
427#[derive(Debug, Clone, Default)]
428pub struct PoolStats {
429 pub total_acquires: usize,
430 pub total_returns: usize,
431 pub current_size: usize,
432}
433
434impl BufferPoolStats {
435 pub fn total_acquisitions(&self) -> usize {
437 self.solution_stats.total_acquires
438 + self.binding_stats.total_acquires
439 + self.hashmap_stats.total_acquires
440 + self.vector_stats.total_acquires
441 }
442
443 pub fn total_returns(&self) -> usize {
445 self.solution_stats.total_returns
446 + self.binding_stats.total_returns
447 + self.hashmap_stats.total_returns
448 + self.vector_stats.total_returns
449 }
450
451 pub fn hit_rate(&self) -> f64 {
453 let total_acquires = self.total_acquisitions();
454 if total_acquires == 0 {
455 return 0.0;
456 }
457
458 let total_returns = self.total_returns();
459 total_returns as f64 / total_acquires as f64
460 }
461
462 pub fn performance_summary(&self) -> String {
464 format!(
465 "Buffer Pool Stats: {} acquisitions, {} returns, {:.2}% hit rate",
466 self.total_acquisitions(),
467 self.total_returns(),
468 self.hit_rate() * 100.0
469 )
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476
477 #[test]
478 fn test_buffer_pool_manager() {
479 let manager = BufferPoolManager::new();
480
481 {
483 let mut pooled_solution = manager.acquire_solution();
484 let solution = pooled_solution.get_mut();
485
486 let mut binding = Binding::new();
488 binding.insert(
489 Variable::new("x").unwrap(),
490 Term::Iri(oxirs_core::model::NamedNode::new_unchecked(
491 "http://example.org/test",
492 )),
493 );
494 solution.push(binding);
495
496 assert_eq!(solution.len(), 1);
497 } {
501 let mut pooled_binding = manager.acquire_binding();
502 let binding = pooled_binding.get_mut();
503
504 binding.insert(
505 Variable::new("y").unwrap(),
506 Term::Iri(oxirs_core::model::NamedNode::new_unchecked(
507 "http://example.org/test2",
508 )),
509 );
510
511 assert_eq!(binding.len(), 1);
512 } let stats = manager.get_stats();
516 assert!(stats.total_acquisitions() > 0);
517
518 println!("Buffer pool stats: {}", stats.performance_summary());
519 }
520
521 #[test]
522 fn test_buffer_pool_memory_management() {
523 let manager = BufferPoolManager::with_capacities(10, 10, 10, 10);
524
525 {
527 let _solutions: Vec<_> = (0..5).map(|_| manager.acquire_solution()).collect();
528 }
530
531 let estimated_memory = manager.estimate_total_memory_usage();
533 assert!(
534 estimated_memory > 0,
535 "Expected memory usage > 0, got {estimated_memory}"
536 );
537
538 manager.clear_all_pools();
540 let memory_after_clear = manager.estimate_total_memory_usage();
541 assert_eq!(memory_after_clear, 0);
542 }
543}