1use std::collections::{BTreeMap, BTreeSet};
7
8use timely::PartialOrder;
9use timely::progress::{Antichain, Timestamp};
10use timely::progress::frontier::AntichainRef;
11
12use crate::difference::Semigroup;
13use crate::lattice::Lattice;
14use crate::trace::{BatchReader, Description};
15use super::ProxyBridge;
16use crate::operators::reduce::ReduceTactic;
17
18use super::history::IdHistory;
19use crate::operators::common::{discover_times, tile_descriptions, DiscoverScratch, KeyView};
20
21pub struct ReduceInstance<'a, B1: BatchReader, B2: BatchReader<Time = B1::Time>> {
23 pub source_batches: &'a [B1],
25 pub input_batches: &'a [B1],
27 pub output_batches: &'a [B2],
29 pub lower: AntichainRef<'a, B1::Time>,
31}
32
33pub struct ReduceWindow<T, RIn, ROut> {
37 pub keys: Vec<u64>,
39 pub input: ProxyBridge<T, RIn>,
41 pub output: ProxyBridge<T, ROut>,
43}
44
45pub trait ProxyReduceBackend<B1: BatchReader, B2: BatchReader<Time = B1::Time>> {
51 type RIn: Semigroup;
53 type ROut: Semigroup + 'static;
55
56 fn seed_times(&self, instance: &ReduceInstance<'_, B1, B2>) -> Vec<(u64, B1::Time)>;
60
61 fn begin(&mut self, tiles: &[Description<B1::Time>]);
67
68 fn next_window(&mut self, instance: &ReduceInstance<'_, B1, B2>, changed: &[u64], cursor: &mut usize) -> Option<ReduceWindow<B1::Time, Self::RIn, Self::ROut>>;
74
75 fn reduce_corrections(
81 &mut self,
82 keys: &[u64],
83 in_ends: &[usize],
84 input: &[(u64, Self::RIn)],
85 out_ends: &[usize],
86 output: &[(u64, Self::ROut)],
87 ) -> (Vec<(u64, Self::ROut)>, Vec<usize>);
88
89 fn emit(&mut self, tile: usize, records: &[((u64, u64), B1::Time, Self::ROut)]);
94
95 fn finish(&mut self) -> Vec<B2>;
97}
98
99pub struct ProxyReduceTactic<T, Bk> {
101 backend: Bk,
102 pending: BTreeMap<u64, Vec<T>>,
104}
105
106impl<T, Bk> ProxyReduceTactic<T, Bk> {
107 pub fn new(backend: Bk) -> Self {
109 ProxyReduceTactic { backend, pending: BTreeMap::new() }
110 }
111}
112
113impl<B1, B2, Bk> ReduceTactic<B1, B2> for ProxyReduceTactic<B1::Time, Bk>
114where
115 B1: BatchReader,
116 B2: BatchReader<Time = B1::Time>,
117 Bk: ProxyReduceBackend<B1, B2>,
118{
119 fn retire(
120 &mut self,
121 source_batches: Vec<B1>,
122 output_batches: Vec<B2>,
123 input_batches: Vec<B1>,
124 lower: &Antichain<B1::Time>,
125 upper: &Antichain<B1::Time>,
126 held: &Antichain<B1::Time>,
127 ) -> (Vec<(B1::Time, B2)>, Antichain<B1::Time>) {
128 if held.elements().iter().all(|t| upper.less_equal(t)) {
129 return (Vec::new(), held.clone());
130 }
131
132 let instance = ReduceInstance {
133 source_batches: &source_batches,
134 input_batches: &input_batches,
135 output_batches: &output_batches,
136 lower: lower.borrow(),
137 };
138
139 let seeds = self.backend.seed_times(&instance);
140 debug_assert!(seeds.windows(2).all(|w| w[0].0 <= w[1].0), "seed_times must be sorted by key_hash");
141 let mut changed: BTreeSet<u64> = seeds.iter().map(|(k, _)| *k).collect();
142 changed.extend(self.pending.keys().copied());
143 if changed.is_empty() {
144 self.pending.clear();
145 return (Vec::new(), Antichain::new());
146 }
147 let changed: Vec<u64> = changed.into_iter().collect();
148
149 let held_elems: Vec<B1::Time> = held.elements().to_vec();
152 let (tile_descs, tile_held, tile_of) = tile_descriptions(lower, upper, &held_elems);
153 self.backend.begin(&tile_descs);
154
155 let mut new_pending: BTreeMap<u64, Vec<B1::Time>> = BTreeMap::new();
156
157 let mut cursor = 0usize;
158 let mut ns = 0usize;
159
160 let mut discover_scratch: DiscoverScratch<B1::Time, Bk::RIn> = DiscoverScratch::new();
163 let mut states: Vec<KeyState<B1::Time, Bk::RIn, Bk::ROut>> = Vec::new();
164 let mut tile_deltas: Vec<Vec<((u64, u64), B1::Time, Bk::ROut)>> = (0..held_elems.len()).map(|_| Vec::new()).collect();
165 let mut batch_keys: Vec<u64> = Vec::new();
166 let mut in_ends: Vec<usize> = Vec::new();
167 let mut in_all: Vec<(u64, Bk::RIn)> = Vec::new();
168 let mut out_ends: Vec<usize> = Vec::new();
169 let mut out_all: Vec<(u64, Bk::ROut)> = Vec::new();
170 let mut active: Vec<(usize, B1::Time)> = Vec::new();
171 let mut in_accum: Vec<(u64, Bk::RIn)> = Vec::new();
172 let mut cur_out: Vec<(u64, Bk::ROut)> = Vec::new();
173 let mut moments_scratch: Vec<B1::Time> = Vec::new();
174 let mut pended_scratch: Vec<B1::Time> = Vec::new();
175
176 while let Some(window) = self.backend.next_window(&instance, &changed, &mut cursor) {
177 let p_in = &window.input;
178 let p_out = &window.output;
179 super::debug_assert_sorted_bridge(p_in, "next_window.input");
180 super::debug_assert_sorted_bridge(p_out, "next_window.output");
181
182 for deltas in tile_deltas.iter_mut() { deltas.clear(); }
183
184 let mut n_states = 0usize;
191 let (mut is, mut os) = (0usize, 0usize);
192 for &key in &window.keys {
193 while is < p_in.len() && p_in[is].0.0 < key { is += 1; }
194 let i0 = is;
195 while is < p_in.len() && p_in[is].0.0 == key { is += 1; }
196 let i1 = is;
197 while os < p_out.len() && p_out[os].0.0 < key { os += 1; }
198 let o0 = os;
199 while os < p_out.len() && p_out[os].0.0 == key { os += 1; }
200 let o1 = os;
201 while ns < seeds.len() && seeds[ns].0 < key { ns += 1; }
202 let n0 = ns;
203 while ns < seeds.len() && seeds[ns].0 == key { ns += 1; }
204 let n1 = ns;
205
206 moments_scratch.clear();
207 pended_scratch.clear();
208 {
209 let pending = self.pending.get(&key).map(|p| &p[..]).unwrap_or(&[]);
210 let seed_times = seeds[n0..n1].iter().map(|(_, t)| t.clone());
211 let out_times = (o0..o1).map(|o| p_out[o].1.clone());
212 discover_times(
213 KeyView { p_in: &p_in[..], i0, i1, pending },
214 seed_times, out_times, upper,
215 &mut discover_scratch,
216 &mut moments_scratch, &mut pended_scratch,
217 );
218 }
219 if !pended_scratch.is_empty() {
220 new_pending.insert(key, std::mem::take(&mut pended_scratch));
221 }
222 if moments_scratch.is_empty() {
223 continue;
224 }
225
226 if n_states == states.len() {
229 states.push(KeyState::empty());
230 }
231 let st = &mut states[n_states];
232 st.key = key;
233 st.cursor = 0;
234 st.produced.clear();
235 st.moments.clear();
236 st.moments.append(&mut moments_scratch);
237 st.meets.clear();
238 st.meets.extend(st.moments.iter().cloned());
239 for i in (1..st.meets.len()).rev() {
240 let m = st.meets[i].clone();
241 st.meets[i - 1].meet_assign(&m);
242 }
243 st.in_replay.load_iter((i0..i1).map(|i| (p_in[i].0.1, p_in[i].1.clone(), p_in[i].2.clone())), st.meets.first());
244 st.out_replay.load_iter((o0..o1).map(|o| (p_out[o].0.1, p_out[o].1.clone(), p_out[o].2.clone())), st.meets.first());
245 n_states += 1;
246 }
247
248 loop {
256 batch_keys.clear();
257 in_ends.clear();
258 in_all.clear();
259 out_ends.clear();
260 out_all.clear();
261 active.clear();
262 let mut advanced = false;
263 for (si, st) in states[..n_states].iter_mut().enumerate() {
264 if st.cursor >= st.moments.len() {
265 continue;
266 }
267 advanced = true;
268 let j = st.cursor;
269 st.cursor += 1;
270 let t = st.moments[j].clone();
271 st.in_replay.step_through(&t);
272 st.out_replay.step_through(&t);
273 st.in_replay.advance_buffer_by(&st.meets[j]);
274 st.out_replay.advance_buffer_by(&st.meets[j]);
275 for ((_, et), _) in st.produced.iter_mut() {
276 *et = et.join(&st.meets[j]);
277 }
278 crate::consolidation::consolidate(&mut st.produced);
279
280 in_accum.clear();
281 for ((vid, et), d) in st.in_replay.buffer().iter() {
282 if et.less_equal(&t) {
283 in_accum.push((*vid, d.clone()));
284 }
285 }
286 crate::consolidation::consolidate(&mut in_accum);
287 cur_out.clear();
288 for ((vid, et), d) in st.out_replay.buffer().iter().chain(st.produced.iter()) {
289 if et.less_equal(&t) {
290 cur_out.push((*vid, d.clone()));
291 }
292 }
293 crate::consolidation::consolidate(&mut cur_out);
294
295 if in_accum.is_empty() && cur_out.is_empty() {
296 continue;
297 }
298 batch_keys.push(st.key);
299 in_all.append(&mut in_accum);
300 in_ends.push(in_all.len());
301 out_all.append(&mut cur_out);
302 out_ends.push(out_all.len());
303 active.push((si, t));
304 }
305 if !advanced {
309 break;
310 }
311 if batch_keys.is_empty() {
312 continue;
313 }
314
315 let (corr, corr_ends) = self.backend.reduce_corrections(&batch_keys, &in_ends, &in_all, &out_ends, &out_all);
316 let mut cstart = 0usize;
317 for (bi, (si, t)) in active.iter().enumerate() {
318 let cend = corr_ends[bi];
319 if cstart != cend {
320 let idx = held_elems.iter().rposition(|h| h.less_equal(t)).expect("no held capability <= active time");
321 for (vid, d) in &corr[cstart..cend] {
322 states[*si].produced.push(((*vid, t.clone()), d.clone()));
323 tile_deltas[idx].push(((states[*si].key, *vid), t.clone(), d.clone()));
324 }
325 }
326 cstart = cend;
327 }
328 }
329
330 for (held_index, deltas) in tile_deltas.iter_mut().enumerate() {
331 if deltas.is_empty() {
332 continue;
333 }
334 if let Some(tile) = tile_of[held_index] {
335 crate::consolidation::consolidate_updates(deltas);
336 self.backend.emit(tile, &deltas[..]);
337 }
338 }
339 }
340
341 self.pending = new_pending;
342 let produced: Vec<(B1::Time, B2)> = tile_held.into_iter().zip(self.backend.finish()).collect();
343 let mut frontier = Antichain::new();
344 for times in self.pending.values() {
345 for t in times {
346 frontier.insert_ref(t);
347 }
348 }
349 (produced, frontier)
350 }
351}
352
353struct KeyState<T, RIn, ROut> {
359 key: u64,
360 moments: Vec<T>,
361 meets: Vec<T>,
362 in_replay: IdHistory<T, RIn>,
363 out_replay: IdHistory<T, ROut>,
364 produced: Vec<((u64, T), ROut)>,
365 cursor: usize,
366}
367
368impl<T: Timestamp + Lattice, RIn: Semigroup, ROut: Semigroup> KeyState<T, RIn, ROut> {
369 fn empty() -> Self {
373 KeyState { key: 0, moments: Vec::new(), meets: Vec::new(), in_replay: IdHistory::new(), out_replay: IdHistory::new(), produced: Vec::new(), cursor: 0 }
374 }
375}
376