1use super::*;
2
3pub enum Part<'a, T, S> { Items(&'a [T]), Total(usize, S) }
5
6pub fn fold_pieces<T: Copy, R: Reduction<T>>(
8 totals: &mut [R::Total], dims: [usize; 3], run: &(dyn Fn(Range<usize>, &mut dyn FnMut(usize, Part<'_, T, R::Total>)) + Sync)) -> bool {
9 let [outer, len, inner] = dims;
10 let (n, unit, least) = (outer * len * inner, len * inner, piece_size(inner));
12 let size = n.div_ceil((n / least).clamp(1, parallel::threads())).max(least).next_multiple_of(CHUNK);
13 let mut pieces = Vec::with_capacity(n.div_ceil(size));
14 let (mut rest, mut taken) = (&mut *totals, 0);
15 for start in (0..n).step_by(size) {
16 let end = n.min(start + size);
17 let (lo, hi) = (start.div_ceil(unit), (end / unit).max(start.div_ceil(unit)));
18 let (_, after) = std::mem::take(&mut rest).split_at_mut((lo - taken) * inner);
19 let (own, after) = after.split_at_mut((hi - lo) * inner);
20 (rest, taken) = (after, hi);
21 let (front, back) = (end.min(lo * unit), end.min(lo * unit).max(hi * unit));
22 let partial = |at: usize| (at / unit, vec![R::identity(); inner]);
23 pieces.push(Piece { range: start..end, lo, hi, own, front: (start < front).then(|| partial(start)), back: (back < end).then(|| partial(back)) });
24 }
25 let mut fits = parallel::pieces_of(&mut pieces, 1, usize::MAX, &|_, pieces| pieces.iter_mut().fold(true, |ok, piece| ok & piece.fold::<T, R>(dims, run)));
26 let partials: Vec<_> = pieces.into_iter().flat_map(|piece| [piece.front, piece.back]).flatten().collect();
27 let mut last = usize::MAX;
28 for (u, part) in partials {
29 let folds = &mut totals[u * inner..(u + 1) * inner];
30 if u == last { fits &= merges_of::<T, R>(folds, &part); } else {
31 folds.copy_from_slice(&part);
32 last = u;
33 }
34 }
35 fits
36}
37
38struct Piece<'a, S> { range: Range<usize>, lo: usize, hi: usize, own: &'a mut [S], front: Option<(usize, Vec<S>)>, back: Option<(usize, Vec<S>)> }
40
41impl<S: Copy> Piece<'_, S> {
42 fn fold<T: Copy, R: Reduction<T, Total = S>>(
44 &mut self, dims: [usize; 3], run: &(dyn Fn(Range<usize>, &mut dyn FnMut(usize, Part<'_, T, S>)) + Sync)) -> bool {
45 let (unit, mut fits) = (dims[1] * dims[2], true);
46 run(self.range.clone(), &mut |mut start, part| match part {
47 Part::Items(mut items) => {
48 while !items.is_empty() {
49 let (folds, at, end) = self.place(start, unit);
50 let (block, after) = items.split_at((end - start).min(items.len()));
51 fits &= fold_into::<T, R>(folds, dims, at, block);
52 (start, items) = (start + block.len(), after);
53 }
54 }
55 Part::Total(n, total) => {
56 let (folds, at, end) = self.place(start, unit);
57 assert!(start + n <= end, "a block's total joins the folds of one part of a piece");
58 fits &= fold_total::<T, R>(folds, dims, at, n, total);
59 }
60 });
61 fits
62 }
63
64 fn place(&mut self, start: usize, unit: usize) -> (&mut [S], usize, usize) {
66 let (a, b) = (self.lo * unit, self.hi * unit);
67 match (start < a, start < b) {
68 (true, _) => self.front.as_mut().map(|(u, f)| (&mut f[..], start - *u * unit, a)).expect("a piece that starts inside a unit has a front fold"),
69 (_, true) => (&mut *self.own, start - a, b),
70 _ => self.back.as_mut().map(|(u, f)| (&mut f[..], start - *u * unit, usize::MAX)).expect("a piece that ends inside a unit has a back fold"),
71 }
72 }
73}
74
75fn fold_total<T, R: Reduction<T>>(totals: &mut [R::Total], [_, len, inner]: [usize; 3], start: usize, n: usize, total: R::Total) -> bool {
77 assert!(inner == 1 && start % len + n <= len, "a block's total covers items of one lane along the last axis");
78 let (i, at) = (start / len, start % len);
79 if at == 0 { totals[i] = total; return true; }
80 let (t, fits) = R::merge(totals[i], total);
81 totals[i] = t;
82 fits
83}
84
85pub(super) fn lanes<T: Copy, R: Reduction<T>>(totals: &mut [R::Total], first: usize, data: &[T], [_, len, inner]: [usize; 3]) -> bool {
87 if inner == 1 { return R::rows(&data[first * len..(first + totals.len()) * len], len, totals); }
88 let (mut rest, mut k, mut fits) = (totals, first, true);
89 let streamed = size_of_val(data) > STREAMED_BYTES;
90 while !rest.is_empty() {
91 let (i, c) = (k / inner, k % inner);
92 let n = (inner - c).min(rest.len());
93 let (folds, after) = std::mem::take(&mut rest).split_at_mut(n);
94 let group = &data[i * len * inner + c..(i + 1) * len * inner];
95 fits &= if folds.len() == inner { step_rows::<T, R>(folds, group, true, streamed) } else { columns_by::<T, R>(folds, group, inner, true, streamed) };
96 (rest, k) = (after, k + folds.len());
97 }
98 fits
99}
100
101#[inline(always)]
103fn step_row<T: Copy, R: Reduction<T>>(totals: &mut [R::Total], row: &[T], first: bool) -> bool {
104 if !first { return steps_of::<T, R>(totals, row); }
105 let mut ok = true;
106 for (t, &x) in totals.iter_mut().zip(row) { *t = one_of::<T, R>(x, &mut ok) }
107 ok
108}
109
110#[inline(always)]
112fn step_rows<T: Copy, R: Reduction<T>>(totals: &mut [R::Total], rows: &[T], first: bool, streamed: bool) -> bool {
113 if !R::COMMUTATIVE { return rows_by::<T, R, 1>(totals, rows, first, streamed); }
114 match crate::slice::running(size_of::<R::Total>()) {
115 8 => rows_by::<T, R, 8>(totals, rows, first, streamed), 16 => rows_by::<T, R, 16>(totals, rows, first, streamed),
116 _ => rows_by::<T, R, 32>(totals, rows, first, streamed)
117 }
118}
119
120#[inline(always)]
122fn rows_by<T: Copy, R: Reduction<T>, const N: usize>(totals: &mut [R::Total], rows: &[T], first: bool, streamed: bool) -> bool {
123 let inner = totals.len();
124 if inner > N { return columns_by::<T, R>(totals, rows, inner, first, streamed); }
125 if rows.len() < 2 * N || rows.len() < 4 * inner {
126 return rows.chunks_exact(inner).enumerate().fold(true, |fits, (j, row)| fits & step_row::<T, R>(totals, row, first && j == 0));
127 }
128 match inner {
129 _ if N % inner == 0 => blocks_by::<T, R, N>(totals, rows, first, N), 3 => blocks_by::<T, R, N>(totals, rows, first, 3 * (N / 4)),
130 _ => blocks_by::<T, R, N>(totals, rows, first, N - N % inner)
131 }
132}
133
134const COLUMN_BYTES: usize = 64 << 10;
136const STREAMED_BYTES: usize = 40 << 20;
138
139fn columns_by<T: Copy, R: Reduction<T>>(totals: &mut [R::Total], rows: &[T], inner: usize, first: bool, streamed: bool) -> bool {
141 let (len, width) = (rows.len().div_ceil(inner), (COLUMN_BYTES / size_of::<R::Total>()).max(1));
142 totals.chunks_mut(width).enumerate().fold(true, |mut fits, (b, block)| {
143 let (at, w) = (b * width, block.len());
144 let row = |j: usize| &rows[j * inner + at..j * inner + at + w];
145 let mut j = usize::from(first && len > 0);
146 if j == 1 { fits &= step_row::<T, R>(block, row(0), true); }
147 while !streamed && j + 4 <= len {
148 fits &= steps_of4::<T, R>(block, [row(j), row(j + 1), row(j + 2), row(j + 3)]);
149 j += 4;
150 }
151 (j..len).fold(fits, |fits, j| fits & steps_of::<T, R>(block, row(j)))
152 })
153}
154
155#[inline(always)]
157fn steps_of4<T: Copy, R: Reduction<T>>(totals: &mut [R::Total], rows: [&[T]; 4]) -> bool {
158 let n = totals.len();
159 let ([a, b, c, d], mut fits) = (rows.map(|r| &r[..n]), true);
160 for k in 0..n {
161 let (t, f) = R::checked(totals[k], a[k]);
162 let (t, g) = R::checked(t, b[k]);
163 let (t, h) = R::checked(t, c[k]);
164 let (t, i) = R::checked(t, d[k]);
165 (totals[k], fits) = (t, fits & f & g & h & i);
166 }
167 fits
168}
169
170#[inline(always)]
172fn blocks_by<T: Copy, R: Reduction<T>, const N: usize>(totals: &mut [R::Total], rows: &[T], first: bool, width: usize) -> bool {
173 let inner = totals.len();
174 let (blocks, rest) = rows.split_at(rows.len() - rows.len() % width);
175 let (mut run, mut fits) = ([R::identity(); N], true);
177 for block in blocks.chunks_exact(width) { fits &= steps_of::<T, R>(&mut run[..width], block) }
178 let mut all = run;
179 let parts = &mut all[..width];
180 fits &= steps_of::<T, R>(parts, rest);
181 for (c, total) in totals.iter_mut().enumerate() {
182 let (column, ok) = parts[c..].iter().step_by(inner).fold((R::identity(), true), |(s, ok), &p| {
183 let (t, f) = R::merge(s, p);
184 (t, ok & f)
185 });
186 let (t, f) = if first { (column, ok) } else { let (t, f) = R::merge(*total, column); (t, f & ok) };
187 (*total, fits) = (t, fits & f);
188 }
189 fits
190}
191
192pub fn fold_into<T: Copy, R: Reduction<T>>(totals: &mut [R::Total], [outer, len, inner]: [usize; 3], start: usize, block: &[T]) -> bool {
194 let (mut rest, mut fits) = (block, true);
195 if rest.is_empty() { return true; }
196 if inner == 1 {
197 let (mut i, at) = (start / len, start % len);
198 if at > 0 {
200 let (piece, after) = rest.split_at((len - at).min(rest.len()));
201 let (s, f) = R::slice(piece);
202 let (t, g) = R::merge(totals[i], s);
203 (totals[i], fits) = (t, f & g);
204 if at + piece.len() < len { return fits; }
205 (rest, i) = (after, i + 1);
206 }
207 let count = rest.len() / len;
208 let (whole, started) = rest.split_at(count * len);
209 fits &= R::rows(whole, len, &mut totals[i..i + count]);
210 if !started.is_empty() {
212 let (s, f) = R::slice(started);
213 (totals[i + count], fits) = (s, fits & f);
214 }
215 } else {
216 let (row, streamed) = (start / inner, outer * len * inner * size_of::<T>() > STREAMED_BYTES);
217 let (mut i, mut j, mut k) = (row / len, row % len, start % inner);
218 while !rest.is_empty() {
219 let rows = if k == 0 { (len - j).min(rest.len() / inner) } else { 0 };
220 if rows > 0 {
221 let (whole, after) = rest.split_at(rows * inner);
222 fits &= step_rows::<T, R>(&mut totals[i * inner..(i + 1) * inner], whole, j == 0, streamed);
223 (rest, j) = (after, j + rows);
224 } else {
225 let (piece, after) = rest.split_at((inner - k).min(rest.len()));
226 fits &= step_row::<T, R>(&mut totals[i * inner + k..], piece, j == 0);
227 (rest, k) = (after, k + piece.len());
228 if k == inner { (j, k) = (j + 1, 0); }
229 }
230 if j == len { (i, j) = (i + 1, 0); }
231 }
232 }
233 fits
234}
235
236#[inline]
238pub(super) fn fold_rows<T: Copy, R: Reduction<T>>(data: &[T], columns: usize, out: &mut [R::Total]) -> bool {
239 #[inline(always)]
240 fn each<const K: usize, T: Copy, R: Reduction<T>>(data: &[T], out: &mut [R::Total]) -> bool {
241 let mut fits = true;
242 for (o, row) in out.iter_mut().zip(data.as_chunks::<K>().0) {
243 let (t, f) = row[1..].iter().fold(R::one(row[0]), |(s, ok), &x| {
244 let (t, f) = R::checked(s, x);
245 (t, ok & f)
246 });
247 (*o, fits) = (t, fits & f);
248 }
249 fits
250 }
251 macro_rules! short {
252 ($($k:literal)+) => {
253 match columns {
254 $($k => each::<$k, T, R>(data, out),)+
255 _ => out.iter_mut().zip(data.chunks_exact(columns)).fold(true, |fits, (o, row)| { let (t, f) = R::slice(row); *o = t; fits & f }),
256 }
257 };
258 }
259 short!(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
260}
261
262#[inline]
264pub(super) fn rows_side_by_side<T: Copy, R: Reduction<T>>(data: &[T], columns: usize, out: &mut [R::Total]) -> bool {
265 if !(16..SHORT_LANE).contains(&columns) { return fold_rows::<T, R>(data, columns, out); }
266 let whole = out.len() / LANES * LANES;
267 let (head, tail) = out.split_at_mut(whole);
268 side_by_side::<LANES, _, _>(&data[..whole * columns], columns, R::one, R::checked, |_, _| {}, |row, t| head[row] = t)
269 & side_by_side::<1, _, _>(&data[whole * columns..], columns, R::one, R::checked, |_, _| {}, |row, t| tail[row] = t)
270}