use timely::progress::{Antichain, Timestamp};
use timely::progress::frontier::AntichainRef;
use crate::difference::{Multiply, Semigroup};
use crate::lattice::Lattice;
use crate::trace::BatchReader;
use super::ProxyBridge;
use crate::operators::join::{Fresh, JoinTactic};
use super::history::IdHistory;
pub struct JoinInstance<'a, B0: BatchReader, B1: BatchReader<Time = B0::Time>> {
pub batches0: &'a [B0],
pub batches1: &'a [B1],
pub lower: AntichainRef<'a, B0::Time>,
}
pub trait ProxyJoinBackend<B0: BatchReader, B1: BatchReader<Time = B0::Time>> {
type R0: Semigroup + Multiply<Self::R1, Output = Self::ROut>;
type R1: Semigroup;
type ROut: Semigroup;
type Output;
fn present0(&mut self, instance: &JoinInstance<'_, B0, B1>, filter: Option<&[u64]>) -> ProxyBridge<B0::Time, Self::R0>;
fn present1(&mut self, instance: &JoinInstance<'_, B0, B1>, filter: Option<&[u64]>) -> ProxyBridge<B0::Time, Self::R1>;
fn cross(&mut self, instance: &JoinInstance<'_, B0, B1>, left: &[(u64, u64)], right: &[(u64, u64)], times: Vec<B0::Time>, diffs: Vec<Self::ROut>) -> Self::Output;
}
pub struct ProxyJoinTactic<B0, B1, Bk> {
backend: Bk,
_marker: std::marker::PhantomData<(B0, B1)>,
}
impl<B0, B1, Bk> ProxyJoinTactic<B0, B1, Bk> {
pub fn new(backend: Bk) -> Self {
ProxyJoinTactic { backend, _marker: std::marker::PhantomData }
}
}
impl<B0, B1, Bk> JoinTactic<B0, B1, Bk::Output> for ProxyJoinTactic<B0, B1, Bk>
where
B0: BatchReader,
B1: BatchReader<Time = B0::Time>,
Bk: ProxyJoinBackend<B0, B1>,
Bk::Output: 'static,
{
fn prep(&mut self, input0: Vec<B0>, input1: Vec<B1>, fresh: Fresh, meet: B0::Time) -> Box<dyn Iterator<Item = Bk::Output>> {
Box::new(join_prep(&mut self.backend, input0, input1, fresh, meet).into_iter())
}
}
const JOIN_CHUNK: usize = 1 << 20;
fn join_prep<B0, B1, Bk>(backend: &mut Bk, input0: Vec<B0>, input1: Vec<B1>, fresh: Fresh, meet: B0::Time) -> Vec<Bk::Output>
where
B0: BatchReader,
B1: BatchReader<Time = B0::Time>,
Bk: ProxyJoinBackend<B0, B1>,
{
let lower = Antichain::from_elem(meet);
let instance = JoinInstance { batches0: &input0, batches1: &input1, lower: lower.borrow() };
let (p0, p1) = match fresh {
Fresh::Input0 => {
let p0 = backend.present0(&instance, None);
if p0.is_empty() { return Vec::new(); }
let mut keys: Vec<u64> = p0.iter().map(|r| r.0.0).collect();
keys.dedup();
let p1 = backend.present1(&instance, Some(&keys));
(p0, p1)
}
Fresh::Input1 => {
let p1 = backend.present1(&instance, None);
if p1.is_empty() { return Vec::new(); }
let mut keys: Vec<u64> = p1.iter().map(|r| r.0.0).collect();
keys.dedup();
let p0 = backend.present0(&instance, Some(&keys));
(p0, p1)
}
};
if p0.is_empty() || p1.is_empty() { return Vec::new(); }
super::debug_assert_sorted_bridge(&p0, "present0");
super::debug_assert_sorted_bridge(&p1, "present1");
let mut out: Vec<Bk::Output> = Vec::new();
{
let mut flush = |li: &mut Vec<(u64, u64)>, ri: &mut Vec<(u64, u64)>, ot: &mut Vec<B0::Time>, od: &mut Vec<Bk::ROut>| {
out.push(backend.cross(&instance, li.as_slice(), ri.as_slice(), std::mem::take(ot), std::mem::take(od)));
li.clear();
ri.clear();
};
let (mut li, mut ri) = (Vec::new(), Vec::new());
let (mut ot, mut od) = (Vec::new(), Vec::new());
let mut h0 = IdHistory::new();
let mut h1 = IdHistory::new();
let (mut i, mut j) = (0usize, 0usize);
while i < p0.len() && j < p1.len() {
let (ki, kj) = (p0[i].0.0, p1[j].0.0);
if ki < kj {
i += 1;
} else if kj < ki {
j += 1;
} else {
let mut e0 = i;
while e0 < p0.len() && p0[e0].0.0 == ki { e0 += 1; }
let mut e1 = j;
while e1 < p1.len() && p1[e1].0.0 == ki { e1 += 1; }
join_key(ki, &p0, i..e0, &p1, j..e1, &mut h0, &mut h1, &mut li, &mut ri, &mut ot, &mut od, &mut flush);
i = e0;
j = e1;
}
}
if !li.is_empty() {
flush(&mut li, &mut ri, &mut ot, &mut od);
}
}
out
}
#[allow(clippy::too_many_arguments)]
fn join_key<T, R0, R1, RO, F>(
kh: u64,
p0: &ProxyBridge<T, R0>,
r0: std::ops::Range<usize>,
p1: &ProxyBridge<T, R1>,
r1: std::ops::Range<usize>,
h0: &mut IdHistory<T, R0>,
h1: &mut IdHistory<T, R1>,
li: &mut Vec<(u64, u64)>,
ri: &mut Vec<(u64, u64)>,
ot: &mut Vec<T>,
od: &mut Vec<RO>,
flush: &mut F,
) where
T: Lattice + Timestamp,
R0: Semigroup + Multiply<R1, Output = RO> + Clone,
R1: Semigroup + Clone,
F: FnMut(&mut Vec<(u64, u64)>, &mut Vec<(u64, u64)>, &mut Vec<T>, &mut Vec<RO>),
{
if r0.len() < 16 || r1.len() < 16 {
for a in r0 {
for b in r1.clone() {
li.push((kh, p0[a].0.1));
ri.push((kh, p1[b].0.1));
ot.push(p0[a].1.join(&p1[b].1));
od.push(p0[a].2.clone().multiply(&p1[b].2));
if li.len() >= JOIN_CHUNK { flush(li, ri, ot, od); }
}
}
return;
}
h0.load_iter(r0.map(|i| (p0[i].0.1, p0[i].1.clone(), p0[i].2.clone())), None);
h1.load_iter(r1.map(|i| (p1[i].0.1, p1[i].1.clone(), p1[i].2.clone())), None);
crate::operators::common::bilinear_wave(h0, h1, |v0, v1, t, d| {
li.push((kh, v0));
ri.push((kh, v1));
ot.push(t);
od.push(d);
if li.len() >= JOIN_CHUNK {
flush(li, ri, ot, od);
}
});
}