1use std::collections::VecDeque;
17use std::marker::PhantomData;
18use std::rc::Rc;
19
20use timely::Accountable;
21use timely::container::{PushInto, SizableContainer};
22use timely::progress::{Antichain, Timestamp};
23use timely::progress::frontier::AntichainRef;
24
25use crate::consolidation::Consolidate;
26use crate::difference::Semigroup;
27use crate::lattice::Lattice;
28use crate::trace::Navigable;
29use crate::trace::cursor::Cursor;
30use crate::trace::implementations::{BatchContainer, Layout, Vector, WithLayout};
31
32use super::Chunk;
33
34const TARGET: usize = 8192;
36
37pub struct VecChunk<K, V, T, R>(Rc<Vec<((K, V), T, R)>>);
39
40impl<K, V, T, R> Clone for VecChunk<K, V, T, R> {
41 fn clone(&self) -> Self { VecChunk(Rc::clone(&self.0)) }
42}
43impl<K, V, T, R> Default for VecChunk<K, V, T, R> {
44 fn default() -> Self { VecChunk(Rc::new(Vec::new())) }
45}
46
47pub type ChunkSpine<K, V, T, R> = super::ChunkSpine<VecChunk<K, V, T, R>>;
49pub type ChunkBatcher<K, V, T, R> = super::ChunkBatcher<VecChunk<K, V, T, R>>;
52pub type ChunkBuilder<K, V, T, R> = super::ChunkBuilder<VecChunk<K, V, T, R>>;
54
55impl<K: 'static, V: 'static, T: 'static, R: 'static> Accountable for VecChunk<K, V, T, R> {
56 fn record_count(&self) -> i64 { self.0.len() as i64 }
57}
58
59impl<K, V, T, R> SizableContainer for VecChunk<K, V, T, R>
60where K: Clone+'static, V: Clone+'static, T: Clone+'static, R: Clone+'static {
61 fn at_capacity(&self) -> bool { self.0.len() >= TARGET }
64 fn ensure_capacity(&mut self, _stash: &mut Option<Self>) {
65 let inner = Rc::make_mut(&mut self.0);
66 inner.reserve(TARGET.saturating_sub(inner.len()));
67 }
68}
69
70impl<K, V, T, R> Consolidate for VecChunk<K, V, T, R>
71where K: Ord+Clone+'static, V: Ord+Clone+'static, T: Ord+Clone+'static, R: Semigroup+'static {
72 fn len(&self) -> usize { self.0.len() }
73 fn clear(&mut self) { Rc::make_mut(&mut self.0).clear() }
74 fn consolidate_into(&mut self, target: &mut Self) {
75 Rc::make_mut(&mut self.0).consolidate_into(Rc::make_mut(&mut target.0));
76 }
77}
78
79impl<K, V, T, R> PushInto<((K, V), T, R)> for VecChunk<K, V, T, R>
80where K: Clone+'static, V: Clone+'static, T: Clone+'static, R: Clone+'static {
81 fn push_into(&mut self, item: ((K, V), T, R)) { Rc::make_mut(&mut self.0).push(item); }
82}
83
84fn gallop<U>(s: &[U], start: usize, pred: impl Fn(&U) -> bool) -> usize {
88 let mut pos = start;
89 if pos < s.len() && pred(&s[pos]) {
90 let mut step = 1;
91 while pos + step < s.len() && pred(&s[pos + step]) { pos += step; step <<= 1; }
92 step >>= 1;
93 while step > 0 {
94 if pos + step < s.len() && pred(&s[pos + step]) { pos += step; }
95 step >>= 1;
96 }
97 pos += 1;
98 }
99 pos
100}
101
102pub struct VecChunkCursor<K, V, T, R> {
105 key_pos: usize,
106 val_pos: usize,
107 phantom: PhantomData<(K, V, T, R)>,
108}
109
110impl<K, V, T, R> WithLayout for VecChunk<K, V, T, R>
111where K: Ord+Clone+'static, V: Ord+Clone+'static, T: Lattice+Timestamp, R: Ord+Semigroup+'static {
112 type Layout = Vector<((K, V), T, R)>;
113}
114
115impl<K, V, T, R> WithLayout for VecChunkCursor<K, V, T, R>
116where K: Ord+Clone+'static, V: Ord+Clone+'static, T: Lattice+Timestamp, R: Ord+Semigroup+'static {
117 type Layout = Vector<((K, V), T, R)>;
118}
119
120impl<K, V, T, R> Cursor for VecChunkCursor<K, V, T, R>
121where K: Ord+Clone+'static, V: Ord+Clone+'static, T: Lattice+Timestamp, R: Ord+Semigroup+'static {
122 type Storage = VecChunk<K, V, T, R>;
123
124 type KeyContainer = <Vector<((K, V), T, R)> as Layout>::KeyContainer;
125 type Key<'a> = <<Vector<((K, V), T, R)> as Layout>::KeyContainer as BatchContainer>::ReadItem<'a>;
126 type ValContainer = <Vector<((K, V), T, R)> as Layout>::ValContainer;
127 type Val<'a> = <<Vector<((K, V), T, R)> as Layout>::ValContainer as BatchContainer>::ReadItem<'a>;
128 type ValOwn = <<Vector<((K, V), T, R)> as Layout>::ValContainer as BatchContainer>::Owned;
129 type TimeContainer = <Vector<((K, V), T, R)> as Layout>::TimeContainer;
130 type TimeGat<'a> = <<Vector<((K, V), T, R)> as Layout>::TimeContainer as BatchContainer>::ReadItem<'a>;
131 type Time = <<Vector<((K, V), T, R)> as Layout>::TimeContainer as BatchContainer>::Owned;
132 type DiffContainer = <Vector<((K, V), T, R)> as Layout>::DiffContainer;
133 type DiffGat<'a> = <<Vector<((K, V), T, R)> as Layout>::DiffContainer as BatchContainer>::ReadItem<'a>;
134 type Diff = <<Vector<((K, V), T, R)> as Layout>::DiffContainer as BatchContainer>::Owned;
135
136 fn key_valid(&self, s: &Self::Storage) -> bool { self.key_pos < s.0.len() }
137 fn val_valid(&self, s: &Self::Storage) -> bool {
138 self.key_pos < s.0.len() && self.val_pos < s.0.len() && s.0[self.val_pos].0.0 == s.0[self.key_pos].0.0
139 }
140 fn key<'a>(&self, s: &'a Self::Storage) -> &'a K { &s.0[self.key_pos].0.0 }
141 fn val<'a>(&self, s: &'a Self::Storage) -> &'a V { &s.0[self.val_pos].0.1 }
142 fn get_key<'a>(&self, s: &'a Self::Storage) -> Option<&'a K> {
143 if self.key_valid(s) { Some(self.key(s)) } else { None }
144 }
145 fn get_val<'a>(&self, s: &'a Self::Storage) -> Option<&'a V> {
146 if self.val_valid(s) { Some(self.val(s)) } else { None }
147 }
148 fn map_times<L: FnMut(&T, &R)>(&mut self, s: &Self::Storage, mut logic: L) {
149 if !self.val_valid(s) { return; }
150 let kv = &s.0[self.val_pos].0;
151 let mut i = self.val_pos;
152 while i < s.0.len() && &s.0[i].0 == kv {
153 logic(&s.0[i].1, &s.0[i].2);
154 i += 1;
155 }
156 }
157 fn step_key(&mut self, s: &Self::Storage) {
158 if self.key_pos >= s.0.len() { return; }
161 let key = s.0[self.key_pos].0.0.clone();
162 let mut i = self.key_pos;
163 while i < s.0.len() && s.0[i].0.0 == key { i += 1; }
164 self.key_pos = i;
165 self.val_pos = i;
166 }
167 fn seek_key(&mut self, s: &Self::Storage, key: &K) {
168 self.key_pos = gallop(&s.0, self.key_pos, |u| &u.0.0 < key);
170 self.val_pos = self.key_pos;
171 }
172 fn step_val(&mut self, s: &Self::Storage) {
173 if !self.val_valid(s) { return; }
174 let kv = s.0[self.val_pos].0.clone();
175 let mut i = self.val_pos;
176 while i < s.0.len() && s.0[i].0 == kv { i += 1; }
177 self.val_pos = i;
178 }
179 fn seek_val(&mut self, s: &Self::Storage, val: &V) {
180 if !self.key_valid(s) { return; }
181 let key = s.0[self.key_pos].0.0.clone();
182 self.val_pos = gallop(&s.0, self.val_pos, |u| (&u.0.0, &u.0.1) < (&key, val));
183 }
184 fn rewind_keys(&mut self, _s: &Self::Storage) { self.key_pos = 0; self.val_pos = 0; }
185 fn rewind_vals(&mut self, _s: &Self::Storage) { self.val_pos = self.key_pos; }
186}
187
188fn take<K: Clone, V: Clone, T: Clone, R: Clone>(chunk: VecChunk<K, V, T, R>) -> Vec<((K, V), T, R)> {
190 Rc::try_unwrap(chunk.0).unwrap_or_else(|rc| (*rc).clone())
191}
192
193impl<K, V, T, R> Navigable for VecChunk<K, V, T, R>
194where K: Ord+Clone+'static, V: Ord+Clone+'static, T: Lattice+Timestamp, R: Ord+Semigroup+'static {
195 type Cursor = VecChunkCursor<K, V, T, R>;
196
197 fn cursor(&self) -> Self::Cursor {
198 VecChunkCursor { key_pos: 0, val_pos: 0, phantom: PhantomData }
199 }
200}
201
202impl<K, V, T, R> super::NavigableChunk for VecChunk<K, V, T, R>
203where K: Ord+Clone+'static, V: Ord+Clone+'static, T: Lattice+Timestamp, R: Ord+Semigroup+'static {
204 fn bounds(&self) -> ((&K, &V, &T), (&K, &V, &T)) {
205 let s = &self.0[..];
206 let (f, l) = (&s[0], &s[s.len() - 1]);
207 ((&f.0.0, &f.0.1, &f.1), (&l.0.0, &l.0.1, &l.1))
208 }
209}
210
211impl<K, V, T, R> Chunk for VecChunk<K, V, T, R>
212where K: Ord+Clone+'static, V: Ord+Clone+'static, T: Lattice+Timestamp, R: Ord+Semigroup+'static {
213 type Time = <<Vector<((K, V), T, R)> as Layout>::TimeContainer as BatchContainer>::Owned;
214
215 const TARGET: usize = TARGET;
216
217 fn len(&self) -> usize { self.0.len() }
218
219 fn merge(in1: &mut VecDeque<Self>, in2: &mut VecDeque<Self>, out: &mut VecDeque<Self>) {
225 fn kv<K, V, T, R>(u: &((K, V), T, R)) -> (&K, &V) { (&u.0.0, &u.0.1) }
226
227 let mut result: Vec<((K, V), T, R)> = Vec::with_capacity(TARGET);
228 let mut flush = |result: &mut Vec<((K, V), T, R)>, force: bool| {
229 if result.len() >= TARGET || (force && !result.is_empty()) {
230 out.push_back(VecChunk(Rc::new(std::mem::replace(result, Vec::with_capacity(TARGET)))));
231 }
232 };
233
234 let mut c1 = in1.pop_front().unwrap();
238 let mut c2 = in2.pop_front().unwrap();
239 let (mut p1, mut p2) = (0usize, 0usize);
240 while p1 < c1.0.len() && p2 < c2.0.len() {
241 let a = &c1.0[p1];
242 let b = &c2.0[p2];
243 match (kv(a), &a.1).cmp(&(kv(b), &b.1)) {
244 std::cmp::Ordering::Less => {
247 let run = gallop(&c1.0[..], p1 + 1, |u| (kv(u), &u.1) < (kv(b), &b.1));
248 for piece in c1.0[p1..run].chunks(TARGET) {
249 result.extend_from_slice(piece);
250 flush(&mut result, false);
251 }
252 p1 = run;
253 }
254 std::cmp::Ordering::Greater => {
255 let run = gallop(&c2.0[..], p2 + 1, |u| (kv(u), &u.1) < (kv(a), &a.1));
256 for piece in c2.0[p2..run].chunks(TARGET) {
257 result.extend_from_slice(piece);
258 flush(&mut result, false);
259 }
260 p2 = run;
261 }
262 std::cmp::Ordering::Equal => {
263 let mut diff = a.2.clone();
264 diff.plus_equals(&b.2);
265 if !diff.is_zero() {
266 result.push((a.0.clone(), a.1.clone(), diff));
267 }
268 p1 += 1;
269 p2 += 1;
270 flush(&mut result, false);
271 }
272 }
273 if p1 == c1.0.len() {
275 match in1.pop_front() { Some(c) => { c1 = c; p1 = 0; } None => break }
276 }
277 if p2 == c2.0.len() {
278 match in2.pop_front() { Some(c) => { c2 = c; p2 = 0; } None => break }
279 }
280 }
281 flush(&mut result, true);
282 if p1 < c1.0.len() { in1.push_front(VecChunk(Rc::new(c1.0[p1..].to_vec()))); }
285 if p2 < c2.0.len() { in2.push_front(VecChunk(Rc::new(c2.0[p2..].to_vec()))); }
286 }
287
288 fn extract(
289 input: &mut VecDeque<Self>,
290 frontier: AntichainRef<T>,
291 residual: &mut Antichain<T>,
292 keep: &mut VecDeque<Self>,
293 ship: &mut VecDeque<Self>,
294 ) {
295 let Some(chunk) = input.pop_front() else { return };
299 let (mut k, mut s) = (Vec::new(), Vec::new());
300 for u in take(chunk) {
301 if frontier.less_equal(&u.1) { residual.insert_ref(&u.1); k.push(u); }
302 else { s.push(u); }
303 }
304 if !k.is_empty() { keep.push_back(VecChunk(Rc::new(k))); }
305 if !s.is_empty() { ship.push_back(VecChunk(Rc::new(s))); }
306 }
307
308 fn advance(
309 input: &mut VecDeque<Self>,
310 frontier: AntichainRef<T>,
311 done: bool,
312 out: &mut VecDeque<Self>,
313 ) {
314 let mut stash: Vec<Vec<((K, V), T, R)>> = Vec::new();
318 let mut buf = match input.pop_front() { Some(chunk) => take(chunk), None => return };
322 while let Some(chunk) = input.pop_front() {
323 let mut v = take(chunk);
324 buf.append(&mut v);
325 stash.push(v);
326 }
327 if buf.is_empty() { return; }
328
329 if !done && buf[0].0 == buf[buf.len() - 1].0 {
333 input.push_front(VecChunk(Rc::new(buf)));
334 return;
335 }
336
337 let end = if done { buf.len() } else {
339 let last_kv = buf[buf.len() - 1].0.clone();
340 let mut start = buf.len();
341 while start > 0 && buf[start - 1].0 == last_kv { start -= 1; }
342 start
343 };
344 if end < buf.len() {
345 input.push_front(VecChunk(Rc::new(buf.split_off(end))));
346 }
347 let mut result = stash.pop().unwrap_or_default();
350 let mut i = 0;
351 while i < buf.len() {
352 let mut j = i;
353 while j < buf.len() && buf[j].0 == buf[i].0 { j += 1; }
354 for u in &mut buf[i..j] { u.1.advance_by(frontier); }
355 buf[i..j].sort_by(|a, b| a.1.cmp(&b.1));
357 let mut k = i;
358 while k < j {
359 let kv = buf[k].0.clone();
360 let t = buf[k].1.clone();
361 let mut diff = buf[k].2.clone();
362 k += 1;
363 while k < j && buf[k].1 == t { diff.plus_equals(&buf[k].2); k += 1; }
364 if !diff.is_zero() {
365 result.push((kv, t, diff));
366 if result.len() >= TARGET { out.push_back(VecChunk(Rc::new(std::mem::replace(&mut result, stash.pop().unwrap_or_default())))); }
367 }
368 }
369 i = j;
370 }
371 if !result.is_empty() { out.push_back(VecChunk(Rc::new(result))); }
372 }
373
374 fn settle(input: &mut VecDeque<Self>, done: bool, out: &mut VecDeque<Self>) {
379 super::pack(
380 input, done, out,
381 |acc, next| Rc::make_mut(&mut acc.0).extend(take(next)),
382 |chunk, n| { let mut rows = take(chunk); let rest = rows.split_off(n); (VecChunk(Rc::new(rows)), VecChunk(Rc::new(rest))) },
383 |chunk| chunk,
384 );
385 }
386}
387
388#[cfg(test)]
389mod test {
390 use std::collections::VecDeque;
391 use super::{Chunk, VecChunk};
392 use crate::trace::chunk::merge_chains;
393 use crate::trace::Navigable;
394 use std::rc::Rc;
395
396 fn chunk(updates: Vec<((u64, u64), u64, i64)>) -> VecChunk<u64, u64, u64, i64> {
397 VecChunk(Rc::new(updates))
398 }
399
400 fn flat<I: IntoIterator<Item = VecChunk<u64, u64, u64, i64>>>(chunks: I) -> Vec<((u64, u64), u64, i64)> {
402 chunks.into_iter().flat_map(|c| (*c.0).clone()).collect()
403 }
404
405 #[test]
408 fn extract_partitions_and_grades() {
409 use super::TARGET;
410 use crate::trace::chunk::{is_graded, settle_all};
411 use timely::progress::Antichain;
412
413 let n = 4 * TARGET as u64;
416 let mut input: VecDeque<_> = (0..n).map(|i| chunk(vec![((i, 0), i % 2, 1)])).collect();
417 let frontier = Antichain::from_elem(1u64);
418 let mut residual = Antichain::new();
419 let (mut keep, mut ship) = (VecDeque::new(), VecDeque::new());
420 while !input.is_empty() {
422 VecChunk::extract(&mut input, frontier.borrow(), &mut residual, &mut keep, &mut ship);
423 }
424 let (keep, ship) = (settle_all(keep), settle_all(ship));
425
426 assert_eq!(residual, Antichain::from_elem(1u64));
428 assert!(is_graded(&keep), "ungraded keep: {:?}", keep.iter().map(Chunk::len).collect::<Vec<_>>());
430 assert!(is_graded(&ship), "ungraded ship: {:?}", ship.iter().map(Chunk::len).collect::<Vec<_>>());
431 assert_eq!(keep.iter().map(Chunk::len).sum::<usize>(), n as usize / 2);
433 assert_eq!(ship.iter().map(Chunk::len).sum::<usize>(), n as usize / 2);
434 }
435
436 #[test]
439 fn advance_emits_complete_groups_eagerly() {
440 use timely::progress::Antichain;
441
442 let frontier = Antichain::from_elem(5u64);
443 let c0 = chunk(vec![((0, 0), 0, 1), ((0, 0), 1, 1), ((1, 0), 0, 1)]);
445 let mut input: VecDeque<_> = VecDeque::from([c0]);
446 let mut out = VecDeque::new();
447 VecChunk::advance(&mut input, frontier.borrow(), false, &mut out);
448
449 assert_eq!(input.len(), 1);
451 assert_eq!(Chunk::len(&input[0]), 1);
452 assert_eq!(flat(out), vec![((0, 0), 5, 2)]);
454 }
455
456 #[test]
460 fn advance_resumable_matches_oneshot() {
461 use timely::progress::Antichain;
462
463 let frontier = Antichain::from_elem(3u64);
464 let input = || vec![
466 chunk(vec![((0, 0), 0, 1), ((0, 0), 1, 1), ((1, 0), 0, 1)]),
467 chunk(vec![((1, 0), 5, 1), ((1, 1), 0, 1), ((2, 0), 0, 1)]),
468 chunk(vec![((2, 0), 2, 1), ((2, 0), 9, 1)]),
469 ];
470
471 let oneshot = {
472 let mut q: VecDeque<_> = input().into();
473 let mut out = VecDeque::new();
474 VecChunk::advance(&mut q, frontier.borrow(), false, &mut out);
475 VecChunk::advance(&mut q, frontier.borrow(), true, &mut out);
476 flat(out)
477 };
478 let incremental = {
479 let mut q = VecDeque::new();
480 let mut out = VecDeque::new();
481 for c in input() { q.push_back(c); VecChunk::advance(&mut q, frontier.borrow(), false, &mut out); }
482 VecChunk::advance(&mut q, frontier.borrow(), true, &mut out);
483 flat(out)
484 };
485 assert_eq!(oneshot, incremental);
486 for u in &oneshot { assert!(u.1 >= 3); }
488 }
489
490 #[test]
494 fn advance_single_key_spanning_pushes() {
495 use timely::progress::Antichain;
496
497 let frontier = Antichain::from_elem(100u64);
498 let n = 50u64;
499 let make = || (0..n).map(|t| chunk(vec![((7u64, 0u64), t, 1i64)])).collect::<Vec<_>>();
500
501 let mut q = VecDeque::new();
502 let mut out = VecDeque::new();
503 for c in make() { q.push_back(c); VecChunk::advance(&mut q, frontier.borrow(), false, &mut out); }
504 VecChunk::advance(&mut q, frontier.borrow(), true, &mut out);
505 assert_eq!(flat(out), vec![((7u64, 0u64), 100u64, n as i64)]);
507 }
508
509 #[test]
510 fn merge_chains_consolidates() {
511 let a = chunk(vec![((0, 0), 0, 1), ((1, 0), 0, 1)]);
512 let b = chunk(vec![((0, 0), 0, 1), ((2, 0), 0, 1)]);
513 let mut out = VecDeque::new();
514 merge_chains(vec![a], vec![b], &mut out);
515 assert_eq!(flat(out), vec![((0, 0), 0, 2), ((1, 0), 0, 1), ((2, 0), 0, 1)]);
516 }
517
518 #[test]
522 fn merge_emits_graded_chunks() {
523 use super::TARGET;
524 use crate::trace::chunk::{is_graded, merge_chains, settle_all};
525
526 let n = 4 * TARGET as u64;
528 let evens = chunk((0..n).step_by(2).map(|k| ((k, 0), 0, 1)).collect());
529 let odds = chunk((0..n).step_by(2).map(|k| ((k + 1, 0), 0, 1)).collect());
530
531 let mut out = VecDeque::new();
532 merge_chains(vec![evens], vec![odds], &mut out);
533 let chunks = settle_all(out);
534
535 assert!(is_graded(&chunks), "merge output not graded: {:?}",
536 chunks.iter().map(Chunk::len).collect::<Vec<_>>());
537 let want: Vec<_> = (0..n).map(|k| ((k, 0u64), 0u64, 1i64)).collect();
539 assert_eq!(flat(chunks), want);
540 }
541
542 #[test]
549 fn merge_matches_reference() {
550 use crate::trace::chunk::merge_chains;
551 use crate::consolidation::consolidate_updates;
552
553 let mut seed = 0x2545F4914F6CDD1Du64;
555 let mut rng = move || { seed ^= seed << 13; seed ^= seed >> 7; seed ^= seed << 17; seed };
556
557 fn gen(rng: &mut impl FnMut() -> u64, n: usize) -> Vec<((u64, u64), u64, i64)> {
560 let mut v: Vec<((u64, u64), u64, i64)> = (0..n).map(|_| {
561 let k = rng() % 20; let val = rng() % 3; let t = rng() % 8;
562 let d = if rng() % 4 == 0 { -1 } else { 1 };
563 ((k, val), t, d)
564 }).collect();
565 consolidate_updates(&mut v);
566 v
567 }
568 fn chain(updates: &[((u64, u64), u64, i64)], sz: usize) -> Vec<VecChunk<u64, u64, u64, i64>> {
571 updates.chunks(sz).map(|c| VecChunk(Rc::new(c.to_vec()))).collect()
572 }
573
574 for _ in 0..300 {
575 let n1 = (rng() as usize % 60) + 1;
576 let u1 = gen(&mut rng, n1);
577 let n2 = (rng() as usize % 60) + 1;
578 let u2 = gen(&mut rng, n2);
579 if u1.is_empty() || u2.is_empty() { continue; }
580 let sz = (rng() as usize % 5) + 1; let mut out = VecDeque::new();
583 merge_chains(chain(&u1, sz), chain(&u2, sz), &mut out);
584 let merged = flat(out);
585
586 let mut reference: Vec<_> = u1.iter().chain(u2.iter()).cloned().collect();
587 consolidate_updates(&mut reference);
588
589 assert_eq!(merged, reference, "chunk size {sz}\n u1={u1:?}\n u2={u2:?}");
590 }
591 }
592
593 #[test]
598 fn batch_merger_resumable_matches_reference() {
599 use crate::trace::{Description, Merger};
600 use crate::trace::chunk::{ChunkBatch, ChunkBatchMerger, is_graded};
601 use crate::trace::cursor::Cursor;
602 use crate::consolidation::consolidate_updates;
603 use timely::progress::Antichain;
604
605 let mut seed = 0x9E3779B97F4A7C15u64;
606 let mut rng = move || { seed ^= seed << 13; seed ^= seed >> 7; seed ^= seed << 17; seed };
607
608 fn gen(rng: &mut impl FnMut() -> u64) -> Vec<((u64, u64), u64, i64)> {
611 let n = rng() as usize % 40 + 1;
612 let mut v: Vec<((u64, u64), u64, i64)> = (0..n).map(|_| {
613 let k = rng() % 10; let val = rng() % 3; let t = rng() % 6;
614 let d = if rng() % 4 == 0 { -1 } else { 1 };
615 ((k, val), t, d)
616 }).collect();
617 consolidate_updates(&mut v);
618 v
619 }
620 fn batch(updates: &[((u64, u64), u64, i64)], sz: usize) -> ChunkBatch<VecChunk<u64, u64, u64, i64>> {
622 let chunks: Vec<_> = updates.chunks(sz).map(|c| VecChunk(Rc::new(c.to_vec()))).collect();
623 let desc = Description::new(
624 Antichain::from_elem(0u64), Antichain::from_elem(10u64), Antichain::from_elem(0u64));
625 ChunkBatch::new(chunks, desc)
626 }
627 fn read(b: &ChunkBatch<VecChunk<u64, u64, u64, i64>>) -> Vec<((u64, u64), u64, i64)> {
629 let mut out = Vec::new();
630 let mut c = b.cursor();
631 while c.key_valid(b) {
632 let k = *c.key(b);
633 while c.val_valid(b) {
634 let v = *c.val(b);
635 c.map_times(b, |t, d| out.push(((k, v), *t, *d)));
636 c.step_val(b);
637 }
638 c.step_key(b);
639 }
640 consolidate_updates(&mut out);
641 out
642 }
643
644 for _ in 0..200 {
645 let u1 = gen(&mut rng);
646 let u2 = gen(&mut rng);
647 if u1.is_empty() || u2.is_empty() { continue; }
648 let sz = (rng() as usize % 4) + 1;
649 let f = rng() % 6;
650 let (s1, s2) = (batch(&u1, sz), batch(&u2, sz));
651 let frontier = Antichain::from_elem(f);
652
653 let mut merger = ChunkBatchMerger::new(&s1, &s2, frontier.borrow());
654 loop {
655 let mut fuel = 1isize; merger.work(&s1, &s2, &mut fuel);
657 if fuel > 0 { break; }
658 }
659 let result = merger.done();
660
661 assert!(is_graded(&result.chunks), "ungraded result: {:?}",
663 result.chunks.iter().map(Chunk::len).collect::<Vec<_>>());
664 let got = read(&result);
666 let mut want: Vec<_> = u1.iter().chain(u2.iter()).cloned().collect();
667 for u in want.iter_mut() { u.1 = u.1.max(f); }
668 consolidate_updates(&mut want);
669 assert_eq!(got, want, "fuel-driven merge mismatch\n u1={u1:?}\n u2={u2:?}\n f={f}");
670 }
671 }
672
673 #[test]
677 fn settle_maximal_packing() {
678 use super::TARGET;
679 use crate::trace::chunk::is_graded;
680
681 let t = TARGET;
684 let sizes = [t / 3, t / 3, t / 3, t, t / 2, t / 2, t, 1, t - 1];
685 let total: usize = sizes.iter().sum();
686 let mut key = 0u64;
687 let mut input = VecDeque::new();
688 let mut output = VecDeque::new();
689 for &s in &sizes {
690 let updates: Vec<_> = (0..s).map(|_| { let k = key; key += 1; ((k, 0u64), 0u64, 1i64) }).collect();
691 input.push_back(chunk(updates));
692 VecChunk::settle(&mut input, false, &mut output);
693 }
694 VecChunk::settle(&mut input, true, &mut output);
695 let chunks: Vec<_> = output.into();
696
697 assert!(is_graded(&chunks), "not graded: {:?}",
698 chunks.iter().map(Chunk::len).collect::<Vec<_>>());
699 let got: Vec<_> = chunks.into_iter().flat_map(|c| (*c.0).clone()).collect();
701 assert_eq!(got.len(), total);
702 assert!(got.windows(2).all(|w| w[0].0.0 < w[1].0.0));
703 }
704
705 #[test]
709 fn cursor_handles_straddle() {
710 use crate::trace::cursor::Cursor;
711 use crate::trace::Description;
712 use crate::trace::chunk::ChunkBatch;
713 use timely::progress::Antichain;
714
715 let chunks = vec![
716 chunk(vec![((0, 0), 0, 1), ((1, 0), 0, 1), ((1, 1), 0, 1)]),
717 chunk(vec![((1, 1), 1, 1), ((1, 2), 0, 1)]),
718 chunk(vec![((2, 0), 0, 1)]),
719 ];
720 let desc = Description::new(
721 Antichain::from_elem(0u64),
722 Antichain::from_elem(2u64),
723 Antichain::from_elem(0u64),
724 );
725 let batch = ChunkBatch::new(chunks, desc);
726
727 let mut cursor = batch.cursor();
728 let got = cursor.to_vec(&batch, |k| *k, |v| *v);
729 let want = vec![
730 ((0u64, 0u64), vec![(0u64, 1i64)]),
731 ((1, 0), vec![(0, 1)]),
732 ((1, 1), vec![(0, 1), (1, 1)]),
733 ((1, 2), vec![(0, 1)]),
734 ((2, 0), vec![(0, 1)]),
735 ];
736 assert_eq!(got, want);
737 }
738
739 #[test]
742 #[ignore]
743 fn seek_microbench() {
744 use std::time::Instant;
745 use std::hint::black_box;
746 use super::gallop;
747 let n = 1_000_000u64;
748 let data: Vec<((u64, ()), u64, isize)> = (0..n).map(|k| ((3 * k, ()), 0u64, 1isize)).collect();
749 for probes in [100u64, 10_000, 1_000_000] {
750 let targets: Vec<u64> = (0..probes).map(|i| 3 * (i * n / probes)).collect();
751 let best = |f: &dyn Fn() -> u64| {
752 let mut b = std::time::Duration::MAX;
753 for _ in 0..5 { let t = Instant::now(); black_box(f()); b = b.min(t.elapsed()); }
754 b
755 };
756 let data = black_box(&data[..]);
757 let g = best(&|| {
758 let (mut pos, mut acc) = (0usize, 0u64);
759 for &tgt in &targets { pos = gallop(data, pos, |u| u.0.0 < tgt); acc += pos as u64; }
760 acc
761 });
762 let l = best(&|| {
763 let (mut pos, mut acc) = (0usize, 0u64);
764 for &tgt in &targets { while pos < data.len() && data[pos].0.0 < tgt { pos += 1; } acc += pos as u64; }
765 acc
766 });
767 eprintln!("probes={probes:>7}: gallop={g:>12?} linear={l:>12?}");
768 }
769 }
770
771 #[test]
776 fn seek_key_hint_is_direction_independent() {
777 use crate::trace::cursor::Cursor;
778 use crate::trace::Description;
779 use crate::trace::chunk::ChunkBatch;
780 use timely::progress::Antichain;
781
782 let chunks: Vec<_> = (0..20u64).map(|k| chunk(vec![((2 * k, 0u64), 0u64, 1i64)])).collect();
784 let desc = Description::new(
785 Antichain::from_elem(0u64), Antichain::from_elem(1u64), Antichain::from_elem(0u64));
786 let batch = ChunkBatch::new(chunks, desc);
787
788 let oracle = |tgt: u64| { let e = tgt + (tgt & 1); (e <= 38).then_some(e) };
790 for start in 0..=40u64 {
791 for tgt in 0..=40u64 {
792 let mut c = batch.cursor();
793 c.seek_key(&batch, &start);
794 c.seek_key(&batch, &tgt);
795 assert_eq!(c.get_key(&batch).copied(), oracle(tgt), "start={start} tgt={tgt}");
796 }
797 }
798 }
799
800}