Skip to main content

differential_dataflow/trace/implementations/
mod.rs

1//! Implementations of `Trace` and associated traits.
2//!
3//! The `Trace` trait provides access to an ordered collection of `(key, val, time, diff)` tuples, but
4//! there is substantial flexibility in implementations of this trait. Depending on characteristics of
5//! the data, we may wish to represent the data in different ways. This module contains several of these
6//! implementations, and combiners for merging the results of different traces.
7//!
8//! As examples of implementations,
9//!
10//! *  The `trie` module is meant to represent general update tuples, with no particular assumptions made
11//!    about their contents. It organizes the data first by key, then by val, and then leaves the rest
12//!    in an unordered pile.
13//!
14//! *  The `keys` module is meant for collections whose value type is `()`, which is to say there is no
15//!    (key, val) structure on the records; all of them are just viewed as "keys".
16//!
17//! *  The `time` module is meant for collections with a single time value. This can remove repetition
18//!    from the representation, at the cost of requiring more instances and run-time merging.
19//!
20//! *  The `base` module is meant for collections with a single time value equivalent to the least time.
21//!    These collections must always accumulate to non-negative collections, and as such we can indicate
22//!    the frequency of an element by its multiplicity. This removes both the time and weight from the
23//!    representation, but is only appropriate for a subset (often substantial) of the data.
24//!
25//! Each of these representations is best suited for different data, but they can be combined to get the
26//! benefits of each, as appropriate. There are several `Cursor` combiners, `CursorList` and `CursorPair`,
27//! for homogeneous and inhomogeneous cursors, respectively.
28//!
29//! #Musings
30//!
31//! What is less clear is how to transfer updates between the representations at merge time in a tasteful
32//! way. Perhaps we could put an ordering on the representations, each pair with a dominant representation,
33//! and part of merging the latter filters updates into the former. Although back and forth might be
34//! appealing, more thinking is required to negotiate all of these policies.
35//!
36//! One option would be to require the layer builder to handle these smarts. Merging is currently done by
37//! the layer as part of custom code, but we could make it simply be "iterate through cursor, push results
38//! into 'ordered builder'". Then the builder would be bright enough to emit a "batch" for the composite
39//! trace, rather than just a batch of the type merged.
40
41pub mod spine_fueled;
42
43pub mod merge_batcher;
44pub mod ord_neu;
45pub mod chunker;
46
47// Opinionated takes on default spines.
48pub use self::chunker::ContainerChunker;
49pub use self::ord_neu::OrdValSpine as ValSpine;
50pub use self::ord_neu::OrdValBatcher as ValBatcher;
51pub use self::ord_neu::RcOrdValBuilder as ValBuilder;
52pub use self::ord_neu::OrdKeySpine as KeySpine;
53pub use self::ord_neu::OrdKeyBatcher as KeyBatcher;
54pub use self::ord_neu::RcOrdKeyBuilder as KeyBuilder;
55
56use std::convert::TryInto;
57
58use serde::{Deserialize, Serialize};
59use timely::container::{DrainContainer, PushInto};
60use timely::progress::Timestamp;
61
62use crate::lattice::Lattice;
63use crate::difference::Semigroup;
64
65/// A type that names constituent update types.
66pub trait Update {
67    /// Key by which data are grouped.
68    type Key: Ord + Clone + 'static;
69    /// Values associated with the key.
70    type Val: Ord + Clone + 'static;
71    /// Time at which updates occur.
72    type Time: Lattice + timely::progress::Timestamp;
73    /// Way in which updates occur.
74    type Diff: Ord + Semigroup + 'static;
75}
76
77impl<K,V,T,R> Update for ((K, V), T, R)
78where
79    K: Ord+Clone+'static,
80    V: Ord+Clone+'static,
81    T: Lattice+timely::progress::Timestamp,
82    R: Ord+Semigroup+'static,
83{
84    type Key = K;
85    type Val = V;
86    type Time = T;
87    type Diff = R;
88}
89
90/// A type with opinions on how updates should be laid out.
91pub trait Layout {
92    /// Container for update keys.
93    type KeyContainer: BatchContainer;
94    /// Container for update vals.
95    type ValContainer: BatchContainer;
96    /// Container for times.
97    type TimeContainer: BatchContainer<Owned: Lattice + timely::progress::Timestamp>;
98    /// Container for diffs.
99    type DiffContainer: BatchContainer<Owned: Semigroup + 'static>;
100    /// Container for offsets.
101    type OffsetContainer: for<'a> BatchContainer<ReadItem<'a> = usize>;
102}
103
104/// A type bearing a layout.
105pub trait WithLayout {
106    /// The layout.
107    type Layout: Layout;
108}
109
110
111// An easy way to provide an explicit layout: as a 5-tuple.
112// Valuable when one wants to perform layout surgery.
113impl<KC, VC, TC, DC, OC> Layout for (KC, VC, TC, DC, OC)
114where
115    KC: BatchContainer,
116    VC: BatchContainer,
117    TC: BatchContainer<Owned: Lattice + timely::progress::Timestamp>,
118    DC: BatchContainer<Owned: Semigroup>,
119    OC: for<'a> BatchContainer<ReadItem<'a> = usize>,
120{
121    type KeyContainer = KC;
122    type ValContainer = VC;
123    type TimeContainer = TC;
124    type DiffContainer = DC;
125    type OffsetContainer = OC;
126}
127
128/// Aliases for types provided by the containers within a `Layout`.
129///
130/// For clarity, prefer `use layout;` and then `layout::Key<L>` to retain the layout context.
131pub mod layout {
132    use crate::trace::implementations::{BatchContainer, Layout};
133
134    /// Alias for an owned key of a layout.
135    pub type Key<L> = <<L as Layout>::KeyContainer as BatchContainer>::Owned;
136    /// Alias for an borrowed key of a layout.
137    pub type KeyRef<'a, L> = <<L as Layout>::KeyContainer as BatchContainer>::ReadItem<'a>;
138    /// Alias for an owned val of a layout.
139    pub type Val<L> = <<L as Layout>::ValContainer as BatchContainer>::Owned;
140    /// Alias for an borrowed val of a layout.
141    pub type ValRef<'a, L> = <<L as Layout>::ValContainer as BatchContainer>::ReadItem<'a>;
142    /// Alias for an owned time of a layout.
143    pub type Time<L> = <<L as Layout>::TimeContainer as BatchContainer>::Owned;
144    /// Alias for an borrowed time of a layout.
145    pub type TimeRef<'a, L> = <<L as Layout>::TimeContainer as BatchContainer>::ReadItem<'a>;
146    /// Alias for an owned diff of a layout.
147    pub type Diff<L> = <<L as Layout>::DiffContainer as BatchContainer>::Owned;
148    /// Alias for an borrowed diff of a layout.
149    pub type DiffRef<'a, L> = <<L as Layout>::DiffContainer as BatchContainer>::ReadItem<'a>;
150}
151
152/// A layout that uses vectors
153pub struct Vector<U: Update> {
154    phantom: std::marker::PhantomData<U>,
155}
156
157impl<U: Update<Diff: Ord>> Layout for Vector<U> {
158    type KeyContainer = Vec<U::Key>;
159    type ValContainer = Vec<U::Val>;
160    type TimeContainer = Vec<U::Time>;
161    type DiffContainer = Vec<U::Diff>;
162    type OffsetContainer = OffsetList;
163}
164
165/// A list of unsigned integers that uses `u32` elements as long as they are small enough, and switches to `u64` once they are not.
166#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Serialize, Deserialize)]
167pub struct OffsetList {
168    /// Length of a prefix of zero elements.
169    pub zero_prefix: usize,
170    /// Offsets that fit within a `u32`.
171    pub smol: Vec<u32>,
172    /// Offsets that either do not fit in a `u32`, or are inserted after some offset that did not fit.
173    pub chonk: Vec<u64>,
174}
175
176impl std::fmt::Debug for OffsetList {
177    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178        f.debug_list().entries(self.into_iter()).finish()
179    }
180}
181
182impl OffsetList {
183    /// Allocate a new list with a specified capacity.
184    pub fn with_capacity(cap: usize) -> Self {
185        Self {
186            zero_prefix: 0,
187            smol: Vec::with_capacity(cap),
188            chonk: Vec::new(),
189        }
190    }
191    /// Inserts the offset, as a `u32` if that is still on the table.
192    pub fn push(&mut self, offset: usize) {
193        if self.smol.is_empty() && self.chonk.is_empty() && offset == 0 {
194            self.zero_prefix += 1;
195        }
196        else if self.chonk.is_empty() {
197            if let Ok(smol) = offset.try_into() {
198                self.smol.push(smol);
199            }
200            else {
201                self.chonk.push(offset.try_into().unwrap())
202            }
203        }
204        else {
205            self.chonk.push(offset.try_into().unwrap())
206        }
207    }
208    /// Like `std::ops::Index`, which we cannot implement as it must return a `&usize`.
209    pub fn index(&self, index: usize) -> usize {
210        if index < self.zero_prefix {
211            0
212        }
213        else if index - self.zero_prefix < self.smol.len() {
214            self.smol[index - self.zero_prefix].try_into().unwrap()
215        }
216        else {
217            self.chonk[index - self.zero_prefix - self.smol.len()].try_into().unwrap()
218        }
219    }
220    /// The number of offsets in the list.
221    pub fn len(&self) -> usize {
222        self.zero_prefix + self.smol.len() + self.chonk.len()
223    }
224}
225
226impl<'a> IntoIterator for &'a OffsetList {
227    type Item = usize;
228    type IntoIter = OffsetListIter<'a>;
229
230    fn into_iter(self) -> Self::IntoIter {
231        OffsetListIter {list: self, index: 0 }
232    }
233}
234
235/// An iterator for [`OffsetList`].
236pub struct OffsetListIter<'a> {
237    list: &'a OffsetList,
238    index: usize,
239}
240
241impl<'a> Iterator for OffsetListIter<'a> {
242    type Item = usize;
243
244    fn next(&mut self) -> Option<Self::Item> {
245        if self.index < self.list.len() {
246            let res = Some(self.list.index(self.index));
247            self.index += 1;
248            res
249        } else {
250            None
251        }
252    }
253}
254
255impl PushInto<usize> for OffsetList {
256    fn push_into(&mut self, item: usize) {
257        self.push(item);
258    }
259}
260
261impl BatchContainer for OffsetList {
262    type Owned = usize;
263    type ReadItem<'a> = usize;
264
265    #[inline(always)]
266    fn into_owned<'a>(item: Self::ReadItem<'a>) -> Self::Owned { item }
267    #[inline(always)]
268    fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> { item }
269
270    fn push_ref(&mut self, item: Self::ReadItem<'_>) { self.push_into(item) }
271    fn push_own(&mut self, item: &Self::Owned) { self.push_into(*item) }
272
273    fn clear(&mut self) { self.zero_prefix = 0; self.smol.clear(); self.chonk.clear(); }
274
275    fn with_capacity(size: usize) -> Self {
276        Self::with_capacity(size)
277    }
278
279    fn merge_capacity(cont1: &Self, cont2: &Self) -> Self {
280        Self::with_capacity(cont1.len() + cont2.len())
281    }
282
283    fn index(&self, index: usize) -> Self::ReadItem<'_> {
284        self.index(index)
285    }
286
287    fn len(&self) -> usize {
288        self.len()
289    }
290}
291
292/// Behavior to split an update into principal components.
293pub trait BuilderInput<K: BatchContainer, V: BatchContainer>: DrainContainer + Sized {
294    /// Key portion
295    type Key<'a>: Ord;
296    /// Value portion
297    type Val<'a>: Ord;
298    /// Time
299    type Time;
300    /// Diff
301    type Diff;
302
303    /// Split an item into separate parts.
304    fn into_parts<'a>(item: Self::Item<'a>) -> (Self::Key<'a>, Self::Val<'a>, Self::Time, Self::Diff);
305
306    /// Test that the key equals a key in the layout's key container.
307    fn key_eq(this: &Self::Key<'_>, other: K::ReadItem<'_>) -> bool;
308
309    /// Test that the value equals a key in the layout's value container.
310    fn val_eq(this: &Self::Val<'_>, other: V::ReadItem<'_>) -> bool;
311
312    /// Count the number of distinct keys, (key, val) pairs, and total updates.
313    fn key_val_upd_counts(chain: &[Self]) -> (usize, usize, usize);
314}
315
316impl<K,KBC,V,VBC,T,R> BuilderInput<KBC, VBC> for Vec<((K, V), T, R)>
317where
318    K: Ord + Clone + 'static,
319    KBC: for<'a> BatchContainer<ReadItem<'a>: PartialEq<&'a K>>,
320    V: Ord + Clone + 'static,
321    VBC: for<'a> BatchContainer<ReadItem<'a>: PartialEq<&'a V>>,
322    T: Timestamp + Lattice + 'static,
323    R: Ord + Semigroup + 'static,
324{
325    type Key<'a> = K;
326    type Val<'a> = V;
327    type Time = T;
328    type Diff = R;
329
330    fn into_parts<'a>(((key, val), time, diff): Self::Item<'a>) -> (Self::Key<'a>, Self::Val<'a>, Self::Time, Self::Diff) {
331        (key, val, time, diff)
332    }
333
334    fn key_eq(this: &K, other: KBC::ReadItem<'_>) -> bool {
335        KBC::reborrow(other) == this
336    }
337
338    fn val_eq(this: &V, other: VBC::ReadItem<'_>) -> bool {
339        VBC::reborrow(other) == this
340    }
341
342    fn key_val_upd_counts(chain: &[Self]) -> (usize, usize, usize) {
343        let mut keys = 0;
344        let mut vals = 0;
345        let mut upds = 0;
346        let mut prev_keyval = None;
347        for link in chain.iter() {
348            for ((key, val), _, _) in link.iter() {
349                if let Some((p_key, p_val)) = prev_keyval {
350                    if p_key != key {
351                        keys += 1;
352                        vals += 1;
353                    } else if p_val != val {
354                        vals += 1;
355                    }
356                } else {
357                    keys += 1;
358                    vals += 1;
359                }
360                upds += 1;
361                prev_keyval = Some((key, val));
362            }
363        }
364        (keys, vals, upds)
365    }
366}
367
368pub use self::containers::{BatchContainer, SliceContainer};
369
370/// Containers for data that resemble `Vec<T>`, with leaner implementations.
371pub mod containers {
372
373    use timely::container::PushInto;
374
375    /// A general-purpose container resembling `Vec<T>`.
376    pub trait BatchContainer: 'static {
377        /// An owned instance of `Self::ReadItem<'_>`.
378        type Owned: Clone + Ord;
379
380        /// The type that can be read back out of the container.
381        type ReadItem<'a>: Copy + Ord;
382
383
384        /// Conversion from an instance of this type to the owned type.
385        #[must_use]
386        fn into_owned<'a>(item: Self::ReadItem<'a>) -> Self::Owned;
387        /// Clones `self` onto an existing instance of the owned type.
388        #[inline(always)]
389        fn clone_onto<'a>(item: Self::ReadItem<'a>, other: &mut Self::Owned) {
390            *other = Self::into_owned(item);
391        }
392
393        /// Push an item into this container
394        fn push_ref(&mut self, item: Self::ReadItem<'_>);
395        /// Push an item into this container
396        fn push_own(&mut self, item: &Self::Owned);
397
398        /// Clears the container. May not release resources.
399        fn clear(&mut self);
400
401        /// Creates a new container with sufficient capacity.
402        fn with_capacity(size: usize) -> Self;
403        /// Creates a new container with sufficient capacity.
404        fn merge_capacity(cont1: &Self, cont2: &Self) -> Self;
405
406        /// Converts a read item into one with a narrower lifetime.
407        fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b>;
408
409        /// Reference to the element at this position.
410        fn index(&self, index: usize) -> Self::ReadItem<'_>;
411
412        /// Reference to the element at this position, if it exists.
413        fn get(&self, index: usize) -> Option<Self::ReadItem<'_>> {
414            if index < self.len() {
415                Some(self.index(index))
416            }
417            else { None }
418        }
419
420        /// Number of contained elements
421        fn len(&self) -> usize;
422        /// Returns the last item if the container is non-empty.
423        fn last(&self) -> Option<Self::ReadItem<'_>> {
424            if self.len() > 0 {
425                Some(self.index(self.len()-1))
426            }
427            else {
428                None
429            }
430        }
431        /// Indicates if the length is zero.
432        fn is_empty(&self) -> bool { self.len() == 0 }
433
434        /// Reports the number of elements satisfying the predicate.
435        ///
436        /// This methods *relies strongly* on the assumption that the predicate
437        /// stays false once it becomes false, a joint property of the predicate
438        /// and the layout of `Self. This allows `advance` to use exponential search to
439        /// count the number of elements in time logarithmic in the result.
440        fn advance<F: for<'a> Fn(Self::ReadItem<'a>)->bool>(&self, start: usize, end: usize, function: F) -> usize {
441
442            let small_limit = 8;
443
444            // Exponential search if the answer isn't within `small_limit`.
445            if end > start + small_limit && function(self.index(start + small_limit)) {
446
447                // start with no advance
448                let mut index = small_limit + 1;
449                if start + index < end && function(self.index(start + index)) {
450
451                    // advance in exponentially growing steps.
452                    let mut step = 1;
453                    while start + index + step < end && function(self.index(start + index + step)) {
454                        index += step;
455                        step <<= 1;
456                    }
457
458                    // advance in exponentially shrinking steps.
459                    step >>= 1;
460                    while step > 0 {
461                        if start + index + step < end && function(self.index(start + index + step)) {
462                            index += step;
463                        }
464                        step >>= 1;
465                    }
466
467                    index += 1;
468                }
469
470                index
471            }
472            else {
473                let limit = std::cmp::min(end, start + small_limit);
474                (start .. limit).filter(|x| function(self.index(*x))).count()
475            }
476        }
477    }
478
479    // All `T: Clone` also implement `ToOwned<Owned = T>`, but without the constraint Rust
480    // struggles to understand why the owned type must be `T` (i.e. the one blanket impl).
481    impl<T: Ord + Clone + 'static> BatchContainer for Vec<T> {
482        type Owned = T;
483        type ReadItem<'a> = &'a T;
484
485        #[inline(always)] fn into_owned<'a>(item: Self::ReadItem<'a>) -> Self::Owned { item.clone() }
486        #[inline(always)] fn clone_onto<'a>(item: Self::ReadItem<'a>, other: &mut Self::Owned) { other.clone_from(item); }
487
488        fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> { item }
489
490        fn push_ref(&mut self, item: Self::ReadItem<'_>) { self.push_into(item) }
491        fn push_own(&mut self, item: &Self::Owned) { self.push_into(item.clone()) }
492
493        fn clear(&mut self) { self.clear() }
494
495        fn with_capacity(size: usize) -> Self {
496            Vec::with_capacity(size)
497        }
498        fn merge_capacity(cont1: &Self, cont2: &Self) -> Self {
499            Vec::with_capacity(cont1.len() + cont2.len())
500        }
501        fn index(&self, index: usize) -> Self::ReadItem<'_> {
502            &self[index]
503        }
504        fn get(&self, index: usize) -> Option<Self::ReadItem<'_>> {
505            <[T]>::get(self, index)
506        }
507        fn len(&self) -> usize {
508            self[..].len()
509        }
510    }
511
512    /// A container that accepts slices `[B::Item]`.
513    pub struct SliceContainer<B> {
514        /// Offsets that bound each contained slice.
515        ///
516        /// The length will be one greater than the number of contained slices,
517        /// starting with zero and ending with `self.inner.len()`.
518        offsets: Vec<usize>,
519        /// An inner container for sequences of `B` that dereferences to a slice.
520        inner: Vec<B>,
521    }
522
523    impl<B: Ord + Clone + 'static> PushInto<&[B]> for SliceContainer<B> {
524        fn push_into(&mut self, item: &[B]) {
525            for x in item.iter() {
526                self.inner.push_into(x);
527            }
528            self.offsets.push(self.inner.len());
529        }
530    }
531
532    impl<B: Ord + Clone + 'static> PushInto<&Vec<B>> for SliceContainer<B> {
533        fn push_into(&mut self, item: &Vec<B>) {
534            self.push_into(&item[..]);
535        }
536    }
537
538    impl<B> BatchContainer for SliceContainer<B>
539    where
540        B: Ord + Clone + Sized + 'static,
541    {
542        type Owned = Vec<B>;
543        type ReadItem<'a> = &'a [B];
544
545        #[inline(always)] fn into_owned<'a>(item: Self::ReadItem<'a>) -> Self::Owned { item.to_vec() }
546        #[inline(always)] fn clone_onto<'a>(item: Self::ReadItem<'a>, other: &mut Self::Owned) { other.clone_from_slice(item); }
547
548        fn reborrow<'b, 'a: 'b>(item: Self::ReadItem<'a>) -> Self::ReadItem<'b> { item }
549
550        fn push_ref(&mut self, item: Self::ReadItem<'_>) { self.push_into(item) }
551        fn push_own(&mut self, item: &Self::Owned) { self.push_into(item) }
552
553        fn clear(&mut self) {
554            self.offsets.clear();
555            self.offsets.push(0);
556            self.inner.clear();
557        }
558
559        fn with_capacity(size: usize) -> Self {
560            let mut offsets = Vec::with_capacity(size + 1);
561            offsets.push(0);
562            Self {
563                offsets,
564                inner: Vec::with_capacity(size),
565            }
566        }
567        fn merge_capacity(cont1: &Self, cont2: &Self) -> Self {
568            let mut offsets = Vec::with_capacity(cont1.inner.len() + cont2.inner.len() + 1);
569            offsets.push(0);
570            Self {
571                offsets,
572                inner: Vec::with_capacity(cont1.inner.len() + cont2.inner.len()),
573            }
574        }
575        fn index(&self, index: usize) -> Self::ReadItem<'_> {
576            let lower = self.offsets[index];
577            let upper = self.offsets[index+1];
578            &self.inner[lower .. upper]
579        }
580        fn len(&self) -> usize {
581            self.offsets.len() - 1
582        }
583    }
584
585    /// Default implementation introduces a first offset.
586    impl<B> Default for SliceContainer<B> {
587        fn default() -> Self {
588            Self {
589                offsets: vec![0],
590                inner: Default::default(),
591            }
592        }
593    }
594}