1use std::collections::{BTreeMap, HashMap};
25use std::rc::Rc;
26
27use timely::container::PushInto;
28use timely::progress::Timestamp;
29use timely::progress::frontier::AntichainRef;
30
31use crate::consolidation::{consolidate, consolidate_updates};
32use crate::difference::Semigroup;
33use crate::lattice::Lattice;
34use crate::trace::chunk::ChunkBatch;
35use crate::trace::chunk::vec::VecChunk;
36use crate::trace::Description;
37
38use super::{KeyPosition, ProxyReduceBackend, ReduceInstance, ReduceWindow};
39
40type VBatch<D, T, R> = Rc<ChunkBatch<VecChunk<u64, D, T, R>>>;
43
44pub struct VecReduceBackend<K, V, W, T, R, L> {
46 logic: L,
48 window_size: usize,
50
51 keys_cache: Vec<u64>,
54 collisions: Vec<u64>,
56 reps: Vec<Option<u64>>,
58
59 in_pool: Vec<(K, V)>,
61 out_pool: Vec<(K, W)>,
63 out_ids: HashMap<(K, W), u64>,
66
67 chunks: Vec<VecChunk<u64, (K, W), T, R>>,
69 stage: Vec<((u64, (K, W)), T, R)>,
71}
72
73impl<K, V, W, T, R, L> VecReduceBackend<K, V, W, T, R, L> {
74 pub fn new(logic: L) -> Self { Self::with_window(logic, 1 << 12) }
76
77 pub fn with_window(logic: L, window_size: usize) -> Self {
79 VecReduceBackend {
80 logic,
81 window_size: window_size.max(1),
82 keys_cache: Vec::new(),
83 collisions: Vec::new(),
84 reps: Vec::new(),
85 in_pool: Vec::new(),
86 out_pool: Vec::new(),
87 out_ids: HashMap::new(),
88 chunks: Vec::new(),
89 stage: Vec::new(),
90 }
91 }
92}
93
94impl<K, V, W, T, R, L> ProxyReduceBackend<T, VBatch<(K, V), T, R>, VBatch<(K, W), T, R>>
95 for VecReduceBackend<K, V, W, T, R, L>
96where
97 K: Ord + Clone + std::hash::Hash + 'static,
98 V: Ord + Clone + 'static,
99 W: Ord + Clone + std::hash::Hash + 'static,
100 T: Lattice + Timestamp,
101 R: Semigroup + Ord + Clone + 'static,
102 L: FnMut(&K, &[(V, R)], &mut Vec<(W, R)>, &mut Vec<(W, R)>),
103{
104 type Key = u64;
105 type VIn = u64;
106 type VOut = u64;
107 type RIn = R;
108 type ROut = R;
109
110 fn begin(&mut self, _description: Description<T>) {
111 self.chunks.clear();
112 }
113
114 #[inline(never)]
115 fn next_window(
116 &mut self,
117 instance: &ReduceInstance<'_, T, VBatch<(K, V), T, R>, VBatch<(K, W), T, R>>,
118 changed: &[u64],
119 from: &mut KeyPosition<u64>,
120 window: &mut ReduceWindow<T, R, R>,
121 ) {
122 let start = match *from { KeyPosition::Start => 0, KeyPosition::At(key) => key, KeyPosition::End => return };
123
124 self.in_pool.clear();
126 self.keys_cache.clear();
127 self.collisions.clear();
128 self.reps.clear();
129 let mut walk = MergedWalk::new(
130 instance.input_batches,
131 instance.source_batches,
132 changed,
133 start,
134 instance.lower,
135 );
136 let mut drawn: Vec<Drawn<(K, V), T, R>> = Vec::new();
137 let mut budget = self.window_size;
138 while let Some(key) = walk.advance(&mut drawn) {
139 self.keys_cache.push(key);
140 budget = budget.saturating_sub(drawn.len());
141 let rep = self.in_pool.len() as u64;
142 let (mut single, mut collides): (Option<&K>, bool) = (None, false);
143 let mut last: Option<&(K, V)> = None;
144 for ((data, time), diff) in drawn.drain(..) {
145 if last != Some(data) {
146 match single {
147 None => single = Some(&data.0),
148 Some(only) => collides |= *only != data.0,
149 }
150 self.in_pool.push(data.clone());
151 last = Some(data);
152 }
153 let id = (self.in_pool.len() - 1) as u64;
154 window.input.push(((key, id), time, diff));
155 }
156 window.seeds.extend(walk.seeds.drain(..).map(|t| (key, t)));
157 self.reps.push(single.map(|_| rep));
158 if collides { self.collisions.push(key); }
159 if budget == 0 { break; }
160 }
161 *from = walk.due().map_or(KeyPosition::End, KeyPosition::At);
162
163 self.out_ids.clear();
165 self.out_pool.clear();
166 let mut owalk = MergedWalk::new(
167 &[],
168 instance.output_batches,
169 &self.keys_cache,
170 start,
171 instance.lower,
172 );
173 let mut odrawn: Vec<Drawn<(K, W), T, R>> = Vec::new();
174 let mut index = 0;
175 while let Some(key) = owalk.advance(&mut odrawn) {
176 debug_assert_eq!(self.keys_cache.get(index), Some(&key), "the output pass replays the input pass's keys");
177 let (mut single, mut collides): (Option<&K>, bool) = (None, false);
178 let mut last: Option<&(K, W)> = None;
179 for ((data, time), diff) in odrawn.drain(..) {
180 if last != Some(data) {
181 match single {
182 None => single = Some(&data.0),
183 Some(only) => collides |= *only != data.0,
184 }
185 self.out_ids.insert(data.clone(), self.out_pool.len() as u64);
186 self.out_pool.push(data.clone());
187 last = Some(data);
188 }
189 let id = (self.out_pool.len() - 1) as u64;
190 window.output.push(((key, id), time, diff));
191 }
192 if let (Some(out_key), Some(rep)) = (single, self.reps.get(index).copied().flatten()) {
195 collides |= self.in_pool[rep as usize].0 != *out_key;
196 }
197 if collides { self.collisions.push(key); }
198 index += 1;
199 }
200 self.collisions.sort_unstable();
202 self.collisions.dedup();
203 }
204
205 #[inline(never)]
206 fn reduce_corrections(
207 &mut self,
208 keys: &[u64],
209 in_ends: &[usize],
210 input: &[(u64, R)],
211 out_ends: &[usize],
212 output: &[(u64, R)],
213 ) -> (Vec<(u64, R)>, Vec<usize>) {
214 let mut corr: Vec<(u64, R)> = Vec::new();
215 let mut corr_ends: Vec<usize> = Vec::with_capacity(keys.len());
216 let (mut is, mut os) = (0usize, 0usize);
217 let mut updates: Vec<(W, R)> = Vec::new();
218 let mut input_vals: Vec<(V, R)> = Vec::new();
219 let mut current: Vec<(W, R)> = Vec::new();
220 for i in 0..keys.len() {
221 let (ie, oe) = (in_ends[i], out_ends[i]);
222 let collides = !self.collisions.is_empty() && self.collisions.binary_search(&keys[i]).is_ok();
224 let single_key: Option<K> = if collides { None } else {
225 input[is..ie].first().map(|(vid, _)| self.in_pool[*vid as usize].0.clone())
226 .or_else(|| output[os..oe].first().map(|(vid, _)| self.out_pool[*vid as usize].0.clone()))
227 };
228 if let Some(key) = single_key {
229 input_vals.clear();
230 input_vals.extend(input[is..ie].iter().map(|(vid, d)| (self.in_pool[*vid as usize].1.clone(), d.clone())));
231 consolidate(&mut input_vals);
232 current.clear();
233 current.extend(output[os..oe].iter().map(|(vid, d)| (self.out_pool[*vid as usize].1.clone(), d.clone())));
234 consolidate(&mut current);
235 updates.clear();
236 (self.logic)(&key, &input_vals, &mut current, &mut updates);
237 consolidate(&mut updates);
238 for (w, d) in updates.drain(..) {
239 let key_w = (key.clone(), w);
240 let id = *self.out_ids.entry(key_w.clone()).or_insert_with(|| {
241 self.out_pool.push(key_w);
242 (self.out_pool.len() - 1) as u64
243 });
244 corr.push((id, d));
245 }
246 } else {
247 let mut ins: BTreeMap<K, Vec<(V, R)>> = BTreeMap::new();
248 for (vid, d) in &input[is..ie] {
249 let (k, v) = &self.in_pool[*vid as usize];
250 ins.entry(k.clone()).or_default().push((v.clone(), d.clone()));
251 }
252 let mut outs: BTreeMap<K, Vec<(W, R)>> = BTreeMap::new();
253 for (vid, d) in &output[os..oe] {
254 let (k, w) = &self.out_pool[*vid as usize];
255 outs.entry(k.clone()).or_default().push((w.clone(), d.clone()));
256 }
257 let mut real_keys: Vec<K> = ins.keys().chain(outs.keys()).cloned().collect();
258 real_keys.sort();
259 real_keys.dedup();
260 for key in real_keys {
261 let mut ivals = ins.remove(&key).unwrap_or_default();
262 consolidate(&mut ivals);
263 let mut cur = outs.remove(&key).unwrap_or_default();
264 consolidate(&mut cur);
265 updates.clear();
266 (self.logic)(&key, &ivals, &mut cur, &mut updates);
267 consolidate(&mut updates);
268 for (w, d) in updates.drain(..) {
269 let key_w = (key.clone(), w);
270 let id = *self.out_ids.entry(key_w.clone()).or_insert_with(|| {
271 self.out_pool.push(key_w);
272 (self.out_pool.len() - 1) as u64
273 });
274 corr.push((id, d));
275 }
276 }
277 }
278 corr_ends.push(corr.len());
279 is = ie;
280 os = oe;
281 }
282 (corr, corr_ends)
283 }
284
285 #[inline(never)]
286 fn emit(&mut self, records: &[((u64, u64), T, R)]) {
287 self.stage.clear();
288 for ((h, vid), t, d) in records {
289 let row = self.out_pool[*vid as usize].clone();
290 self.stage.push(((*h, row), t.clone(), d.clone()));
291 }
292 consolidate_updates(&mut self.stage);
294 let chunks = &mut self.chunks;
295 for update in self.stage.drain(..) {
296 if chunks.last().is_none_or(|c| c.as_slice().len() >= <VecChunk<u64, (K, W), T, R> as crate::trace::chunk::Chunk>::TARGET) {
297 chunks.push(VecChunk::default());
298 }
299 chunks.last_mut().expect("pushed above if absent").push_into(update);
300 }
301 }
302
303 #[inline(never)]
304 fn finish(&mut self) -> Option<VBatch<(K, W), T, R>> {
305 self.in_pool.clear();
306 self.out_pool.clear();
307 self.out_ids.clear();
308 let chunks = std::mem::take(&mut self.chunks);
309 if chunks.is_empty() { None } else { Some(Rc::new(ChunkBatch::new(chunks))) }
310 }
311}
312
313type Drawn<'a, D, T, R> = ((&'a D, T), R);
319
320fn run_len<D, T, R>(slice: &[((u64, D), T, R)], key: u64) -> usize {
326 debug_assert!(slice.first().is_some_and(|r| r.0.0 == key), "the run must start at `key`");
327 let mut step = 1;
328 while step < slice.len() && slice[step].0.0 == key { step <<= 1; }
329 let lower = step >> 1;
330 let upper = step.min(slice.len());
331 lower + slice[lower..upper].partition_point(|r| r.0.0 <= key)
332}
333
334struct MergedWalk<'a, D: Ord + Clone + 'static, T: Lattice + Timestamp, R: Semigroup + Ord + Clone + 'static> {
349 novel: &'a [VBatch<D, T, R>],
351 prior: &'a [VBatch<D, T, R>],
352 changed: &'a [u64],
354 ci: usize,
356 pos: Vec<(usize, usize)>,
358 due: Option<u64>,
360 frontier: AntichainRef<'a, T>,
365 seeds: Vec<T>,
369}
370
371impl<'a, D, T, R> MergedWalk<'a, D, T, R>
372where
373 D: Ord + Clone + 'static,
374 T: Lattice + Timestamp,
375 R: Semigroup + Ord + Clone + 'static,
376{
377 fn new(
379 novel: &'a [VBatch<D, T, R>],
380 prior: &'a [VBatch<D, T, R>],
381 changed: &'a [u64],
382 from: u64,
383 frontier: AntichainRef<'a, T>,
384 ) -> Self {
385 let count = novel.len() + prior.len();
386 let mut walk = MergedWalk {
387 novel,
388 prior,
389 changed,
390 ci: changed.partition_point(|k| *k < from),
391 pos: vec![(0, 0); count],
392 due: None,
393 frontier,
394 seeds: Vec::new(),
395 };
396 for b in 0..count {
397 walk.seek_to(b, from);
398 }
399 walk.refresh();
400 walk
401 }
402
403 fn count(&self) -> usize { self.novel.len() + self.prior.len() }
405
406 fn batch(&self, b: usize) -> &'a ChunkBatch<VecChunk<u64, D, T, R>> {
408 if b < self.novel.len() { &self.novel[b] } else { &self.prior[b - self.novel.len()] }
409 }
410
411 fn head(&self, b: usize) -> Option<u64> {
413 let (chunk, offset) = self.pos[b];
414 self.batch(b).chunks.get(chunk).and_then(|c| c.as_slice().get(offset)).map(|r| r.0.0)
415 }
416
417 fn due(&self) -> Option<u64> { self.due }
423
424 fn refresh(&mut self) {
426 let from_novel = (0..self.novel.len()).filter_map(|b| self.head(b)).min();
427 let from_changed = self.changed.get(self.ci).copied();
428 self.due = [from_novel, from_changed].into_iter().flatten().min();
429 }
430
431 fn seek_to(&mut self, b: usize, key: u64) {
436 if self.head(b).is_none_or(|h| h >= key) { return; }
441 let chunks = &self.batch(b).chunks;
442 let (mut chunk, mut offset) = self.pos[b];
443 let skip = chunks[chunk.min(chunks.len())..]
444 .partition_point(|c| c.as_slice().last().is_some_and(|r| r.0.0 < key));
445 if skip > 0 {
446 chunk += skip;
447 offset = 0;
448 }
449 if let Some(slice) = chunks.get(chunk).map(|c| c.as_slice()) {
450 offset += slice[offset..].partition_point(|r| r.0.0 < key);
451 }
452 self.pos[b] = (chunk, offset);
453 }
454
455 fn take_run(&mut self, b: usize, key: u64, into: &mut Vec<Drawn<'a, D, T, R>>) {
460 let novel = b < self.novel.len();
461 let chunks = &self.batch(b).chunks;
462 while let Some(slice) = chunks.get(self.pos[b].0).map(|c| c.as_slice()) {
463 let start = self.pos[b].1;
464 if slice.get(start).is_none_or(|r| r.0.0 != key) { return; }
468 let end = start + run_len(&slice[start..], key);
469 let run = &slice[start..end];
470 if novel {
472 self.seeds.extend(run.iter().map(|record| record.1.clone()));
473 into.extend(run.iter().map(|record| ((&record.0.1, record.1.clone()), record.2.clone())));
474 } else {
475 into.extend(run.iter().map(|record| {
476 let mut time = record.1.clone();
477 time.advance_by(self.frontier);
478 ((&record.0.1, time), record.2.clone())
479 }));
480 }
481 if end < slice.len() {
482 self.pos[b].1 = end;
483 return;
484 }
485 self.pos[b] = (self.pos[b].0 + 1, 0);
486 }
487 }
488
489 fn advance(&mut self, into: &mut Vec<Drawn<'a, D, T, R>>) -> Option<u64> {
496 let key = self.due?;
497 into.clear();
498 self.seeds.clear();
499 for b in 0..self.count() {
500 self.seek_to(b, key);
501 self.take_run(b, key, into);
502 }
503 consolidate(into);
504 self.seeds.sort();
505 self.seeds.dedup();
506 if self.changed.get(self.ci) == Some(&key) { self.ci += 1; }
507 self.refresh();
508 Some(key)
509 }
510}