1use std::iter::repeat_with;
6use std::sync::atomic::Ordering::Relaxed;
7use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize};
8use std::thread::*;
9
10pub struct ParIter<'a, T> {
11 id: usize,
12 items: &'a [T],
13 workers: &'a [CachePadding],
14}
15
16impl<'a, T> Iterator for ParIter<'a, T> {
17 type Item = &'a T;
18
19 fn next(&mut self) -> Option<&'a T> {
20 let worker = &self.workers[self.id];
22 let current = worker.increment();
23 let (start, end) = unpack(current);
24
25 if start < end {
27 return Some(&self.items[start]);
28 }
29
30 loop {
33 let (other, current, size) = self
36 .workers
37 .iter()
38 .filter_map(|other| {
39 let current = other.load();
40 let (start, end) = unpack(current);
41 let size = end.saturating_sub(start);
42
43 (size > 0).then_some((other, current, size))
44 })
45 .max_by_key(|&(_, _, size)| size)?;
46
47 let (start, end) = unpack(current);
49 let middle = start + size.div_ceil(2);
50
51 let next = pack(middle, end);
52 let stolen = pack(start + 1, middle);
53
54 if other.compare_exchange(current, next) {
57 worker.store(stolen);
58 break Some(&self.items[start]);
59 }
60 }
61 }
62}
63
64#[repr(align(128))]
68pub struct CachePadding {
69 atomic: AtomicUsize,
70}
71
72impl CachePadding {
76 #[inline]
77 fn new(n: usize) -> Self {
78 Self { atomic: AtomicUsize::new(n) }
79 }
80
81 #[inline]
82 fn increment(&self) -> usize {
83 self.atomic.fetch_add(1, Relaxed)
84 }
85
86 #[inline]
87 fn load(&self) -> usize {
88 self.atomic.load(Relaxed)
89 }
90
91 #[inline]
92 fn store(&self, n: usize) {
93 self.atomic.store(n, Relaxed);
94 }
95
96 #[inline]
97 fn compare_exchange(&self, current: usize, new: usize) -> bool {
98 self.atomic.compare_exchange(current, new, Relaxed, Relaxed).is_ok()
99 }
100}
101
102pub struct AtomicIter {
104 running: AtomicBool,
105 index: AtomicU32,
106 step: u32,
107}
108
109impl AtomicIter {
110 pub fn new(start: u32, step: u32) -> Self {
111 Self { running: AtomicBool::new(true), index: AtomicU32::from(start), step }
112 }
113
114 pub fn next(&self) -> Option<u32> {
115 self.running.load(Relaxed).then(|| self.index.fetch_add(self.step, Relaxed))
116 }
117
118 pub fn stop(&self) {
119 self.running.store(false, Relaxed);
120 }
121}
122
123pub fn threads() -> usize {
125 available_parallelism().unwrap().get()
126}
127
128pub fn spawn<F, R>(f: F) -> Vec<R>
130where
131 F: Fn() -> R + Copy + Send,
132 R: Send,
133{
134 scope(|scope| {
135 let handles: Vec<_> = repeat_with(|| scope.spawn(f)).take(threads()).collect();
136 handles.into_iter().flat_map(ScopedJoinHandle::join).collect()
137 })
138}
139
140pub fn spawn_parallel_iterator<F, R, T>(items: &[T], f: F) -> Vec<R>
147where
148 F: Fn(ParIter<'_, T>) -> R + Copy + Send,
149 R: Send,
150 T: Sync,
151{
152 let threads = threads();
153 let size = items.len().div_ceil(threads);
154
155 let workers: Vec<_> = (0..threads)
157 .map(|id| {
158 let start = (id * size).min(items.len());
159 let end = (start + size).min(items.len());
160 CachePadding::new(pack(start, end))
161 })
162 .collect();
163 let workers = workers.as_slice();
164
165 scope(|scope| {
166 let handles: Vec<_> =
167 (0..threads).map(|id| scope.spawn(move || f(ParIter { id, items, workers }))).collect();
168 handles.into_iter().flat_map(ScopedJoinHandle::join).collect()
169 })
170}
171
172#[inline]
173fn pack(start: usize, end: usize) -> usize {
174 (end << 32) | start
175}
176
177#[inline]
178fn unpack(both: usize) -> (usize, usize) {
179 (both & 0xffffffff, both >> 32)
180}