Skip to content

Commit fc348d4

Browse files
committed
timely-util: address extract pass-through review comments
1 parent d1e99b3 commit fc348d4

3 files changed

Lines changed: 33 additions & 17 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/timely-util/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ lz4_flex.workspace = true
4040
mz-ore = { path = "../ore", default-features = false, features = ["async", "process", "tracing", "test", "num-traits", "region", "differential-dataflow", "overflowing", "pager", "pool"] }
4141
num-traits.workspace = true
4242
serde.workspace = true
43+
smallvec.workspace = true
4344
timely.workspace = true
4445
tokio.workspace = true
4546
tracing.workspace = true

src/timely-util/src/columnar/chunk.rs

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
//! resident fence metadata so a probe set faults only the chunk bodies it
4545
//! actually touches.
4646
47+
use std::borrow::Cow;
4748
#[cfg(test)]
4849
use std::cell::Cell;
4950
use std::cell::RefCell;
@@ -58,6 +59,7 @@ use differential_dataflow::lattice::Lattice;
5859
use differential_dataflow::trace::chunk::Chunk;
5960
use mz_ore::cast::CastFrom;
6061
use mz_ore::pool::{ChunkHandle, ChunkHints, ExtentCodec, IDENTITY_CODEC, Pool};
62+
use smallvec::SmallVec;
6163
use timely::Accountable;
6264
use timely::PartialOrder;
6365
use timely::container::{ContainerBuilder, PushInto};
@@ -273,8 +275,9 @@ pub struct SpilledBody<D: Columnar, T> {
273275
time_lower: Antichain<T>,
274276
/// The maximal times in the body. Some contained time is
275277
/// greater-or-equal to a frontier exactly when some maximal time is,
276-
/// which is `extract`'s ship-whole test.
277-
time_upper: Vec<T>,
278+
/// which is `extract`'s ship-whole test. A single element for totally
279+
/// ordered times, hence the inline capacity.
280+
time_upper: SmallVec<[T; 1]>,
278281
/// The chunk's generational depth, mirrored into the pool's
279282
/// [`ChunkHints`] at spill time.
280283
depth: u8,
@@ -418,7 +421,7 @@ impl<D: Columnar, T: Columnar, R: Columnar> ColumnChunk<D, T, R> {
418421
records,
419422
fences,
420423
time_lower,
421-
time_upper,
424+
time_upper: time_upper.into(),
422425
depth,
423426
handle,
424427
}))
@@ -465,16 +468,23 @@ impl<D: Columnar, T: Columnar, R: Columnar> ColumnChunk<D, T, R> {
465468
}
466469
}
467470

468-
/// The chunk's time bounds: stored metadata for spilled bodies, a scan
469-
/// of the time column for resident ones. The scan costs less than the
470-
/// copy it lets `extract` avoid when the chunk passes through whole.
471-
fn chunk_time_bounds(&self) -> (Antichain<T>, Vec<T>)
471+
/// The chunk's time bounds: borrowed from the stored metadata for
472+
/// spilled bodies, computed by a time-column scan for resident ones. The
473+
/// scan costs less than the copy it lets `extract` avoid when the chunk
474+
/// passes through whole.
475+
fn chunk_time_bounds(&self) -> (Cow<'_, Antichain<T>>, Cow<'_, [T]>)
472476
where
473477
T: Timestamp,
474478
{
475479
match self {
476-
ColumnChunk::Resident(col, _) => Self::time_bounds(col),
477-
ColumnChunk::Spilled(body) => (body.time_lower.clone(), body.time_upper.clone()),
480+
ColumnChunk::Resident(col, _) => {
481+
let (lower, upper) = Self::time_bounds(col);
482+
(Cow::Owned(lower), Cow::Owned(upper))
483+
}
484+
ColumnChunk::Spilled(body) => (
485+
Cow::Borrowed(&body.time_lower),
486+
Cow::Borrowed(&body.time_upper[..]),
487+
),
478488
}
479489
}
480490

@@ -486,17 +496,20 @@ impl<D: Columnar, T: Columnar, R: Columnar> ColumnChunk<D, T, R> {
486496
where
487497
T: Timestamp,
488498
{
489-
let view = column.borrow();
490-
let times = view.1;
499+
let (_, times, _) = column.borrow();
491500
let mut lower = Antichain::new();
492501
let mut upper: Vec<T> = Vec::new();
502+
// One owned time reused across the scan, so times with owned
503+
// allocations do not allocate per element; the bound sets clone only
504+
// the elements they retain.
505+
let mut time = T::minimum();
493506
for i in 0..times.len() {
494-
let t = T::into_owned(rr::<T>(times.get(i)));
495-
if !upper.iter().any(|u| PartialOrder::less_equal(&t, u)) {
496-
upper.retain(|u| !PartialOrder::less_equal(u, &t));
497-
upper.push(t.clone());
507+
time.copy_from(rr::<T>(times.get(i)));
508+
if !upper.iter().any(|u| PartialOrder::less_equal(&time, u)) {
509+
upper.retain(|u| !PartialOrder::less_equal(u, &time));
510+
upper.push(time.clone());
498511
}
499-
lower.insert(t);
512+
lower.insert_ref(&time);
500513
}
501514
(lower, upper)
502515
}
@@ -721,7 +734,7 @@ where
721734
// The residual must lower-bound every kept time, which is the
722735
// chunk's lower bound antichain by construction.
723736
for m in time_lower.elements() {
724-
residual.insert(m.clone());
737+
residual.insert_ref(m);
725738
}
726739
keep.push_back(chunk);
727740
return;
@@ -1651,6 +1664,7 @@ mod tests {
16511664
/// Extracting a large chunk at an intermediate frontier cuts both sides
16521665
/// into several chunks and partitions exactly by time.
16531666
#[mz_ore::test]
1667+
#[cfg_attr(miri, ignore)]
16541668
fn extract_cuts_large_output() {
16551669
let records: Vec<Tuple> = (0..300_000u64).map(|k| ((k, 0), k % 2, 1)).collect();
16561670
let mut input = VecDeque::from([ColumnChunk::from_column(build_column(&records))]);

0 commit comments

Comments
 (0)