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) ]
4849use std:: cell:: Cell ;
4950use std:: cell:: RefCell ;
@@ -58,6 +59,7 @@ use differential_dataflow::lattice::Lattice;
5859use differential_dataflow:: trace:: chunk:: Chunk ;
5960use mz_ore:: cast:: CastFrom ;
6061use mz_ore:: pool:: { ChunkHandle , ChunkHints , ExtentCodec , IDENTITY_CODEC , Pool } ;
62+ use smallvec:: SmallVec ;
6163use timely:: Accountable ;
6264use timely:: PartialOrder ;
6365use 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