ruvector_mincut/pool/
mod.rs1use crate::graph::VertexId;
39use std::collections::{HashSet, VecDeque};
40use std::cell::RefCell;
41
42thread_local! {
44 static BFS_POOL: RefCell<BfsPoolInner> = RefCell::new(BfsPoolInner::new());
45}
46
47struct BfsPoolInner {
49 queues: Vec<VecDeque<VertexId>>,
51 visited_sets: Vec<HashSet<VertexId>>,
53 result_vecs: Vec<Vec<VertexId>>,
55 acquires: usize,
57 hits: usize,
59}
60
61impl BfsPoolInner {
62 fn new() -> Self {
63 Self {
64 queues: Vec::new(),
65 visited_sets: Vec::new(),
66 result_vecs: Vec::new(),
67 acquires: 0,
68 hits: 0,
69 }
70 }
71
72 fn acquire_queue(&mut self, capacity: usize) -> VecDeque<VertexId> {
73 self.acquires += 1;
74 if let Some(mut queue) = self.queues.pop() {
75 self.hits += 1;
76 queue.clear();
77 if queue.capacity() < capacity {
79 queue.reserve(capacity - queue.len());
80 }
81 queue
82 } else {
83 VecDeque::with_capacity(capacity)
84 }
85 }
86
87 fn acquire_visited(&mut self, capacity: usize) -> HashSet<VertexId> {
88 self.acquires += 1;
89 if let Some(mut set) = self.visited_sets.pop() {
90 self.hits += 1;
91 set.clear();
92 if set.capacity() < capacity {
93 set.reserve(capacity - set.len());
94 }
95 set
96 } else {
97 HashSet::with_capacity(capacity)
98 }
99 }
100
101 fn acquire_vec(&mut self, capacity: usize) -> Vec<VertexId> {
102 self.acquires += 1;
103 if let Some(mut v) = self.result_vecs.pop() {
104 self.hits += 1;
105 v.clear();
106 if v.capacity() < capacity {
107 v.reserve(capacity - v.len());
108 }
109 v
110 } else {
111 Vec::with_capacity(capacity)
112 }
113 }
114
115 fn return_queue(&mut self, queue: VecDeque<VertexId>) {
116 if self.queues.len() < 8 {
118 self.queues.push(queue);
119 }
120 }
121
122 fn return_visited(&mut self, set: HashSet<VertexId>) {
123 if self.visited_sets.len() < 8 {
124 self.visited_sets.push(set);
125 }
126 }
127
128 fn return_vec(&mut self, v: Vec<VertexId>) {
129 if self.result_vecs.len() < 8 {
130 self.result_vecs.push(v);
131 }
132 }
133}
134
135pub struct BfsResources {
139 pub queue: VecDeque<VertexId>,
141 pub visited: HashSet<VertexId>,
143 pub results: Vec<VertexId>,
145}
146
147impl Drop for BfsResources {
148 fn drop(&mut self) {
149 BFS_POOL.with(|pool| {
151 let mut pool = pool.borrow_mut();
152
153 let queue = std::mem::take(&mut self.queue);
155 let visited = std::mem::take(&mut self.visited);
156 let results = std::mem::take(&mut self.results);
157
158 pool.return_queue(queue);
159 pool.return_visited(visited);
160 pool.return_vec(results);
161 });
162 }
163}
164
165pub struct BfsPool;
169
170impl BfsPool {
171 pub fn acquire(expected_size: usize) -> BfsResources {
200 BFS_POOL.with(|pool| {
201 let mut pool = pool.borrow_mut();
202 BfsResources {
203 queue: pool.acquire_queue(expected_size),
204 visited: pool.acquire_visited(expected_size),
205 results: pool.acquire_vec(expected_size),
206 }
207 })
208 }
209
210 pub fn stats() -> (usize, usize, f64) {
214 BFS_POOL.with(|pool| {
215 let pool = pool.borrow();
216 let rate = if pool.acquires > 0 {
217 pool.hits as f64 / pool.acquires as f64
218 } else {
219 0.0
220 };
221 (pool.acquires, pool.hits, rate)
222 })
223 }
224
225 pub fn clear() {
227 BFS_POOL.with(|pool| {
228 let mut pool = pool.borrow_mut();
229 pool.queues.clear();
230 pool.visited_sets.clear();
231 pool.result_vecs.clear();
232 });
233 }
234}
235
236pub struct DistanceBfsResources {
238 pub queue: VecDeque<(VertexId, usize)>,
240 pub visited: HashSet<VertexId>,
242 pub distances: std::collections::HashMap<VertexId, usize>,
244}
245
246impl Default for DistanceBfsResources {
247 fn default() -> Self {
248 Self::new()
249 }
250}
251
252impl DistanceBfsResources {
253 pub fn new() -> Self {
255 Self {
256 queue: VecDeque::new(),
257 visited: HashSet::new(),
258 distances: std::collections::HashMap::new(),
259 }
260 }
261
262 pub fn with_capacity(capacity: usize) -> Self {
264 Self {
265 queue: VecDeque::with_capacity(capacity),
266 visited: HashSet::with_capacity(capacity),
267 distances: std::collections::HashMap::with_capacity(capacity),
268 }
269 }
270
271 pub fn clear(&mut self) {
273 self.queue.clear();
274 self.visited.clear();
275 self.distances.clear();
276 }
277
278 pub fn bfs_within_radius<F>(
288 &mut self,
289 source: VertexId,
290 radius: usize,
291 adjacency: F,
292 ) -> &HashSet<VertexId>
293 where
294 F: Fn(VertexId) -> Vec<VertexId>,
295 {
296 self.clear();
297
298 self.queue.push_back((source, 0));
299 self.visited.insert(source);
300 self.distances.insert(source, 0);
301
302 while let Some((vertex, dist)) = self.queue.pop_front() {
303 if dist >= radius {
304 continue;
305 }
306
307 for neighbor in adjacency(vertex) {
308 if self.visited.insert(neighbor) {
309 let new_dist = dist + 1;
310 self.distances.insert(neighbor, new_dist);
311 self.queue.push_back((neighbor, new_dist));
312 }
313 }
314 }
315
316 &self.visited
317 }
318}
319
320pub struct CompactBfsResources {
324 pub queue: VecDeque<VertexId>,
326 pub visited: [u64; 4],
328 pub results: Vec<VertexId>,
330}
331
332impl Default for CompactBfsResources {
333 fn default() -> Self {
334 Self::new()
335 }
336}
337
338impl CompactBfsResources {
339 pub fn new() -> Self {
341 Self {
342 queue: VecDeque::with_capacity(32),
343 visited: [0; 4],
344 results: Vec::with_capacity(32),
345 }
346 }
347
348 pub fn clear(&mut self) {
350 self.queue.clear();
351 self.visited = [0; 4];
352 self.results.clear();
353 }
354
355 #[inline]
357 pub fn is_visited(&self, v: VertexId) -> bool {
358 if v >= 256 {
359 return false;
360 }
361 let idx = (v / 64) as usize;
362 let bit = v % 64;
363 (self.visited[idx] & (1u64 << bit)) != 0
364 }
365
366 #[inline]
368 pub fn mark_visited(&mut self, v: VertexId) -> bool {
369 if v >= 256 {
370 return false;
371 }
372 let idx = (v / 64) as usize;
373 let bit = v % 64;
374 let was_visited = (self.visited[idx] & (1u64 << bit)) != 0;
375 self.visited[idx] |= 1u64 << bit;
376 !was_visited
377 }
378
379 pub fn visited_count(&self) -> usize {
381 self.visited.iter().map(|w| w.count_ones() as usize).sum()
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388
389 #[test]
390 fn test_bfs_pool_acquire() {
391 let res = BfsPool::acquire(100);
392 assert!(res.queue.is_empty());
393 assert!(res.visited.is_empty());
394 assert!(res.results.is_empty());
395 }
396
397 #[test]
398 fn test_bfs_pool_reuse() {
399 {
401 let mut res = BfsPool::acquire(100);
402 res.queue.push_back(1);
403 res.queue.push_back(2);
404 res.visited.insert(1);
405 res.visited.insert(2);
406 } let res = BfsPool::acquire(100);
410 assert!(res.queue.is_empty());
411 assert!(res.visited.is_empty());
412 }
413
414 #[test]
415 fn test_bfs_pool_stats() {
416 BfsPool::clear(); let _r1 = BfsPool::acquire(10);
420 let _r2 = BfsPool::acquire(10);
421 drop(_r1);
422 drop(_r2);
423
424 let _r3 = BfsPool::acquire(10);
426
427 let (acquires, hits, _rate) = BfsPool::stats();
428 assert!(acquires >= 3);
429 assert!(hits >= 1); }
431
432 #[test]
433 fn test_distance_bfs() {
434 let mut res = DistanceBfsResources::with_capacity(10);
435
436 let adjacency = |v: VertexId| -> Vec<VertexId> {
438 match v {
439 0 => vec![1],
440 1 => vec![0, 2],
441 2 => vec![1, 3],
442 3 => vec![2, 4],
443 4 => vec![3],
444 _ => vec![],
445 }
446 };
447
448 let visited = res.bfs_within_radius(0, 2, adjacency);
449
450 assert!(visited.contains(&0));
452 assert!(visited.contains(&1));
453 assert!(visited.contains(&2));
454 assert!(!visited.contains(&3)); assert!(!visited.contains(&4));
456
457 assert_eq!(res.distances.get(&0), Some(&0));
459 assert_eq!(res.distances.get(&1), Some(&1));
460 assert_eq!(res.distances.get(&2), Some(&2));
461 }
462
463 #[test]
464 fn test_compact_bfs() {
465 let mut res = CompactBfsResources::new();
466
467 assert!(!res.is_visited(0));
468 assert!(res.mark_visited(0)); assert!(res.is_visited(0));
470 assert!(!res.mark_visited(0)); res.mark_visited(100);
473 res.mark_visited(255);
474
475 assert_eq!(res.visited_count(), 3);
476
477 res.clear();
478 assert_eq!(res.visited_count(), 0);
479 }
480
481 #[test]
482 fn test_compact_bfs_boundary() {
483 let mut res = CompactBfsResources::new();
484
485 assert!(res.mark_visited(0));
487 assert!(res.mark_visited(63));
488 assert!(res.mark_visited(64));
489 assert!(res.mark_visited(127));
490 assert!(res.mark_visited(128));
491 assert!(res.mark_visited(191));
492 assert!(res.mark_visited(192));
493 assert!(res.mark_visited(255));
494
495 assert!(res.is_visited(0));
496 assert!(res.is_visited(255));
497
498 assert!(!res.is_visited(256));
500 assert!(!res.mark_visited(256));
501 }
502}