1use timely::progress::{Antichain, Timestamp};
7use timely::progress::frontier::AntichainRef;
8
9use crate::difference::{Multiply, Semigroup};
10use crate::lattice::Lattice;
11use crate::trace::BatchReader;
12use super::ProxyBridge;
13use crate::operators::join::{Fresh, JoinTactic};
14
15use super::history::IdHistory;
16
17pub struct JoinInstance<'a, B0: BatchReader, B1: BatchReader<Time = B0::Time>> {
19 pub batches0: &'a [B0],
21 pub batches1: &'a [B1],
23 pub lower: AntichainRef<'a, B0::Time>,
25}
26
27pub trait ProxyJoinBackend<B0: BatchReader, B1: BatchReader<Time = B0::Time>> {
32 type R0: Semigroup + Multiply<Self::R1, Output = Self::ROut>;
34 type R1: Semigroup;
36 type ROut: Semigroup;
38 type Output;
40
41 fn present0(&mut self, instance: &JoinInstance<'_, B0, B1>, filter: Option<&[u64]>) -> ProxyBridge<B0::Time, Self::R0>;
45 fn present1(&mut self, instance: &JoinInstance<'_, B0, B1>, filter: Option<&[u64]>) -> ProxyBridge<B0::Time, Self::R1>;
49 fn cross(&mut self, instance: &JoinInstance<'_, B0, B1>, left: &[(u64, u64)], right: &[(u64, u64)], times: Vec<B0::Time>, diffs: Vec<Self::ROut>) -> Self::Output;
51}
52
53pub struct ProxyJoinTactic<B0, B1, Bk> {
55 backend: Bk,
56 _marker: std::marker::PhantomData<(B0, B1)>,
57}
58
59impl<B0, B1, Bk> ProxyJoinTactic<B0, B1, Bk> {
60 pub fn new(backend: Bk) -> Self {
62 ProxyJoinTactic { backend, _marker: std::marker::PhantomData }
63 }
64}
65
66impl<B0, B1, Bk> JoinTactic<B0, B1, Bk::Output> for ProxyJoinTactic<B0, B1, Bk>
67where
68 B0: BatchReader,
69 B1: BatchReader<Time = B0::Time>,
70 Bk: ProxyJoinBackend<B0, B1>,
71 Bk::Output: 'static,
72{
73 fn prep(&mut self, input0: Vec<B0>, input1: Vec<B1>, fresh: Fresh, meet: B0::Time) -> Box<dyn Iterator<Item = Bk::Output>> {
74 Box::new(join_prep(&mut self.backend, input0, input1, fresh, meet).into_iter())
75 }
76}
77
78const JOIN_CHUNK: usize = 1 << 20;
80
81fn join_prep<B0, B1, Bk>(backend: &mut Bk, input0: Vec<B0>, input1: Vec<B1>, fresh: Fresh, meet: B0::Time) -> Vec<Bk::Output>
88where
89 B0: BatchReader,
90 B1: BatchReader<Time = B0::Time>,
91 Bk: ProxyJoinBackend<B0, B1>,
92{
93 let lower = Antichain::from_elem(meet);
94 let instance = JoinInstance { batches0: &input0, batches1: &input1, lower: lower.borrow() };
95
96 let (p0, p1) = match fresh {
99 Fresh::Input0 => {
100 let p0 = backend.present0(&instance, None);
101 if p0.is_empty() { return Vec::new(); }
102 let mut keys: Vec<u64> = p0.iter().map(|r| r.0.0).collect();
103 keys.dedup();
104 let p1 = backend.present1(&instance, Some(&keys));
105 (p0, p1)
106 }
107 Fresh::Input1 => {
108 let p1 = backend.present1(&instance, None);
109 if p1.is_empty() { return Vec::new(); }
110 let mut keys: Vec<u64> = p1.iter().map(|r| r.0.0).collect();
111 keys.dedup();
112 let p0 = backend.present0(&instance, Some(&keys));
113 (p0, p1)
114 }
115 };
116 if p0.is_empty() || p1.is_empty() { return Vec::new(); }
117 super::debug_assert_sorted_bridge(&p0, "present0");
118 super::debug_assert_sorted_bridge(&p1, "present1");
119
120 let mut out: Vec<Bk::Output> = Vec::new();
126 {
127 let mut flush = |li: &mut Vec<(u64, u64)>, ri: &mut Vec<(u64, u64)>, ot: &mut Vec<B0::Time>, od: &mut Vec<Bk::ROut>| {
128 out.push(backend.cross(&instance, li.as_slice(), ri.as_slice(), std::mem::take(ot), std::mem::take(od)));
129 li.clear();
130 ri.clear();
131 };
132
133 let (mut li, mut ri) = (Vec::new(), Vec::new());
134 let (mut ot, mut od) = (Vec::new(), Vec::new());
135 let mut h0 = IdHistory::new();
138 let mut h1 = IdHistory::new();
139 let (mut i, mut j) = (0usize, 0usize);
140 while i < p0.len() && j < p1.len() {
141 let (ki, kj) = (p0[i].0.0, p1[j].0.0);
142 if ki < kj {
143 i += 1;
144 } else if kj < ki {
145 j += 1;
146 } else {
147 let mut e0 = i;
148 while e0 < p0.len() && p0[e0].0.0 == ki { e0 += 1; }
149 let mut e1 = j;
150 while e1 < p1.len() && p1[e1].0.0 == ki { e1 += 1; }
151 join_key(ki, &p0, i..e0, &p1, j..e1, &mut h0, &mut h1, &mut li, &mut ri, &mut ot, &mut od, &mut flush);
152 i = e0;
153 j = e1;
154 }
155 }
156 if !li.is_empty() {
157 flush(&mut li, &mut ri, &mut ot, &mut od);
158 }
159 }
160 out
161}
162
163#[allow(clippy::too_many_arguments)]
169fn join_key<T, R0, R1, RO, F>(
170 kh: u64,
171 p0: &ProxyBridge<T, R0>,
172 r0: std::ops::Range<usize>,
173 p1: &ProxyBridge<T, R1>,
174 r1: std::ops::Range<usize>,
175 h0: &mut IdHistory<T, R0>,
176 h1: &mut IdHistory<T, R1>,
177 li: &mut Vec<(u64, u64)>,
178 ri: &mut Vec<(u64, u64)>,
179 ot: &mut Vec<T>,
180 od: &mut Vec<RO>,
181 flush: &mut F,
182) where
183 T: Lattice + Timestamp,
184 R0: Semigroup + Multiply<R1, Output = RO> + Clone,
185 R1: Semigroup + Clone,
186 F: FnMut(&mut Vec<(u64, u64)>, &mut Vec<(u64, u64)>, &mut Vec<T>, &mut Vec<RO>),
187{
188 if r0.len() < 16 || r1.len() < 16 {
192 for a in r0 {
193 for b in r1.clone() {
194 li.push((kh, p0[a].0.1));
195 ri.push((kh, p1[b].0.1));
196 ot.push(p0[a].1.join(&p1[b].1));
197 od.push(p0[a].2.clone().multiply(&p1[b].2));
198 if li.len() >= JOIN_CHUNK { flush(li, ri, ot, od); }
199 }
200 }
201 return;
202 }
203
204 h0.load_iter(r0.map(|i| (p0[i].0.1, p0[i].1.clone(), p0[i].2.clone())), None);
207 h1.load_iter(r1.map(|i| (p1[i].0.1, p1[i].1.clone(), p1[i].2.clone())), None);
208
209 crate::operators::common::bilinear_wave(h0, h1, |v0, v1, t, d| {
210 li.push((kh, v0));
211 ri.push((kh, v1));
212 ot.push(t);
213 od.push(d);
214 if li.len() >= JOIN_CHUNK {
215 flush(li, ri, ot, od);
216 }
217 });
218}