Skip to content

Commit e3de60b

Browse files
committed
Use aggregate descriptors for zoned stats
Signed-off-by: Nicholas Gates <nick@nickgates.com>
1 parent f14f6a1 commit e3de60b

9 files changed

Lines changed: 646 additions & 197 deletions

File tree

vortex-layout/public-api.lock

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -766,13 +766,9 @@ pub mod vortex_layout::layouts::zoned::writer
766766

767767
pub struct vortex_layout::layouts::zoned::writer::ZonedLayoutOptions
768768

769-
pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::block_size: usize
770-
771-
pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::concurrency: usize
769+
pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::aggregate_fns: alloc::sync::Arc<[vortex_array::aggregate_fn::erased::AggregateFnRef]>
772770

773-
pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::max_variable_length_statistics_size: usize
774-
775-
pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::stats: alloc::sync::Arc<[vortex_array::expr::stats::Stat]>
771+
pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::block_size: usize
776772

777773
impl core::default::Default for vortex_layout::layouts::zoned::writer::ZonedLayoutOptions
778774

@@ -800,7 +796,7 @@ pub fn vortex_layout::layouts::zoned::zone_map::ZoneMap::dtype_for_stats_table(&
800796

801797
pub fn vortex_layout::layouts::zoned::zone_map::ZoneMap::prune(&self, &vortex_array::expr::expression::Expression, &vortex_session::VortexSession) -> vortex_error::VortexResult<vortex_mask::Mask>
802798

803-
pub fn vortex_layout::layouts::zoned::zone_map::ZoneMap::try_new(vortex_array::dtype::DType, vortex_array::arrays::struct_::vtable::StructArray, alloc::sync::Arc<[vortex_array::expr::stats::Stat]>, u64, u64) -> vortex_error::VortexResult<Self>
799+
pub fn vortex_layout::layouts::zoned::zone_map::ZoneMap::try_new(vortex_array::dtype::DType, vortex_array::arrays::struct_::vtable::StructArray, alloc::sync::Arc<[vortex_array::aggregate_fn::erased::AggregateFnRef]>, u64, u64) -> vortex_error::VortexResult<Self>
804800

805801
impl core::clone::Clone for vortex_layout::layouts::zoned::zone_map::ZoneMap
806802

@@ -848,11 +844,11 @@ pub struct vortex_layout::layouts::zoned::ZonedLayout
848844

849845
impl vortex_layout::layouts::zoned::ZonedLayout
850846

851-
pub fn vortex_layout::layouts::zoned::ZonedLayout::new(vortex_layout::LayoutRef, vortex_layout::LayoutRef, usize, alloc::sync::Arc<[vortex_array::expr::stats::Stat]>) -> Self
847+
pub fn vortex_layout::layouts::zoned::ZonedLayout::new(vortex_layout::LayoutRef, vortex_layout::LayoutRef, usize, alloc::sync::Arc<[vortex_array::aggregate_fn::erased::AggregateFnRef]>) -> Self
852848

853849
pub fn vortex_layout::layouts::zoned::ZonedLayout::nzones(&self) -> usize
854850

855-
pub fn vortex_layout::layouts::zoned::ZonedLayout::present_stats(&self) -> &alloc::sync::Arc<[vortex_array::expr::stats::Stat]>
851+
pub fn vortex_layout::layouts::zoned::ZonedLayout::present_aggregates(&self) -> &alloc::sync::Arc<[alloc::string::String]>
856852

857853
pub fn vortex_layout::layouts::zoned::ZonedLayout::zone_len(&self) -> usize
858854

vortex-layout/src/layouts/zoned/builder.rs

Lines changed: 129 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use vortex_array::ExecutionCtx;
1212
use vortex_array::IntoArray;
1313
use vortex_array::LEGACY_SESSION;
1414
use vortex_array::VortexSessionExecute;
15+
use vortex_array::aggregate_fn::AggregateFnRef;
1516
use vortex_array::aggregate_fn::fns::sum::sum;
1617
use vortex_array::arrays::ConstantArray;
1718
use vortex_array::arrays::StructArray;
@@ -25,7 +26,6 @@ use vortex_array::dtype::Nullability;
2526
use vortex_array::dtype::PType;
2627
use vortex_array::expr::stats::Precision;
2728
use vortex_array::expr::stats::Stat;
28-
use vortex_array::expr::stats::StatsProvider;
2929
use vortex_array::scalar::Scalar;
3030
use vortex_array::scalar::ScalarTruncation;
3131
use vortex_array::scalar::lower_bound;
@@ -35,9 +35,12 @@ use vortex_array::validity::Validity;
3535
use vortex_buffer::BufferString;
3636
use vortex_buffer::ByteBuffer;
3737
use vortex_error::VortexResult;
38+
use vortex_error::vortex_ensure_eq;
3839

3940
use crate::layouts::zoned::schema::MAX_IS_TRUNCATED;
4041
use crate::layouts::zoned::schema::MIN_IS_TRUNCATED;
42+
use crate::layouts::zoned::schema::aggregate_descriptor;
43+
use crate::layouts::zoned::schema::aggregate_state_dtype;
4144

4245
/// Accumulates write-time statistics for each logical zone.
4346
pub struct StatsAccumulator {
@@ -74,18 +77,6 @@ impl StatsAccumulator {
7477
}
7578
}
7679

77-
pub fn push_chunk_without_compute(&mut self, array: &ArrayRef) -> VortexResult<()> {
78-
for builder in &mut self.builders {
79-
if let Precision::Exact(value) = array.statistics().get(builder.stat()) {
80-
builder.append_scalar(value.cast(&value.dtype().as_nullable())?)?;
81-
} else {
82-
builder.append_null();
83-
}
84-
}
85-
self.length += 1;
86-
Ok(())
87-
}
88-
8980
pub fn push_chunk(&mut self, array: &ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult<()> {
9081
for builder in &mut self.builders {
9182
if let Some(value) = array.statistics().compute_stat(builder.stat(), ctx)? {
@@ -176,6 +167,102 @@ fn supports_file_stats(dtype: &DType) -> bool {
176167
!matches!(dtype, DType::Variant(_))
177168
}
178169

170+
/// Accumulates aggregate-function partials for each logical zone.
171+
pub(crate) struct AggregateStatsAccumulator {
172+
builders: Vec<AggregateStatsArrayBuilder>,
173+
length: usize,
174+
}
175+
176+
impl AggregateStatsAccumulator {
177+
pub(crate) fn new(dtype: &DType, aggregate_fns: &[AggregateFnRef]) -> Self {
178+
let builders = aggregate_fns
179+
.iter()
180+
.filter_map(|aggregate_fn| {
181+
aggregate_state_dtype(dtype, aggregate_fn).map(|partial_dtype| {
182+
AggregateStatsArrayBuilder::new(
183+
aggregate_fn.clone(),
184+
&partial_dtype.as_nullable(),
185+
1024,
186+
)
187+
})
188+
})
189+
.collect::<Vec<_>>();
190+
191+
Self {
192+
builders,
193+
length: 0,
194+
}
195+
}
196+
197+
pub(crate) fn aggregate_fns(&self) -> Arc<[AggregateFnRef]> {
198+
self.builders
199+
.iter()
200+
.map(|builder| builder.aggregate_fn.clone())
201+
.collect::<Vec<_>>()
202+
.into()
203+
}
204+
205+
pub(crate) fn push_partials(&mut self, partials: Vec<Scalar>) -> VortexResult<()> {
206+
vortex_ensure_eq!(
207+
partials.len(),
208+
self.builders.len(),
209+
"aggregate partial count must match zone stats builder count"
210+
);
211+
212+
for (builder, value) in self.builders.iter_mut().zip_eq(partials) {
213+
builder.append_scalar(value)?;
214+
}
215+
self.length += 1;
216+
Ok(())
217+
}
218+
219+
pub(crate) fn as_array(
220+
&mut self,
221+
) -> VortexResult<Option<(StructArray, Arc<[AggregateFnRef]>)>> {
222+
let mut names = Vec::new();
223+
let mut fields = Vec::new();
224+
let mut aggregate_fns = Vec::new();
225+
226+
for builder in self
227+
.builders
228+
.iter_mut()
229+
.sorted_unstable_by(|lhs, rhs| lhs.descriptor.cmp(&rhs.descriptor))
230+
{
231+
let values = builder.finish();
232+
233+
if values.all_invalid()? {
234+
continue;
235+
}
236+
237+
aggregate_fns.push(builder.aggregate_fn.clone());
238+
names.extend(values.names);
239+
fields.extend(values.arrays);
240+
}
241+
242+
if names.is_empty() {
243+
return Ok(None);
244+
}
245+
246+
let array = StructArray::try_new(names.into(), fields, self.length, Validity::NonNullable)?;
247+
Ok(Some((array, aggregate_fns.into())))
248+
}
249+
}
250+
251+
pub(crate) fn aggregate_partials(
252+
array: &ArrayRef,
253+
aggregate_fns: &[AggregateFnRef],
254+
ctx: &mut ExecutionCtx,
255+
) -> VortexResult<Vec<Scalar>> {
256+
aggregate_fns
257+
.iter()
258+
.map(|aggregate_fn| {
259+
let mut accumulator = aggregate_fn.accumulator(array.dtype())?;
260+
accumulator.accumulate(array, ctx)?;
261+
accumulator.partial_scalar()
262+
})
263+
.collect()
264+
}
265+
179266
fn stats_builder_with_capacity(
180267
stat: Stat,
181268
dtype: &DType,
@@ -214,6 +301,35 @@ fn stats_builder_with_capacity(
214301
}
215302
}
216303

304+
struct AggregateStatsArrayBuilder {
305+
aggregate_fn: AggregateFnRef,
306+
descriptor: String,
307+
dtype: DType,
308+
builder: Box<dyn ArrayBuilder>,
309+
}
310+
311+
impl AggregateStatsArrayBuilder {
312+
fn new(aggregate_fn: AggregateFnRef, dtype: &DType, capacity: usize) -> Self {
313+
Self {
314+
descriptor: aggregate_descriptor(&aggregate_fn),
315+
aggregate_fn,
316+
dtype: dtype.clone(),
317+
builder: builder_with_capacity(dtype, capacity),
318+
}
319+
}
320+
321+
fn append_scalar(&mut self, value: Scalar) -> VortexResult<()> {
322+
self.builder.append_scalar(&value.cast(&self.dtype)?)
323+
}
324+
325+
fn finish(&mut self) -> NamedArrays {
326+
NamedArrays {
327+
names: vec![self.descriptor.clone().into()],
328+
arrays: vec![self.builder.finish()],
329+
}
330+
}
331+
}
332+
217333
/// Arrays with their associated names, reduced version of a `StructArray`.
218334
struct NamedArrays {
219335
names: Vec<FieldName>,

0 commit comments

Comments
 (0)