Skip to content

Commit ded7452

Browse files
Rachel IntRachel Int
authored andcommitted
experiment: avoid final partition reordering
1 parent 91738e3 commit ded7452

3 files changed

Lines changed: 34 additions & 169 deletions

File tree

datafusion/physical-plan/src/aggregates/hash_aggregate.rs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ use super::aggregate_hash_table::{
4242
AggregateHashTable, FinalMarker, PartialMarker, PartialSkipMarker,
4343
};
4444
use super::skip_partial::SkipAggregationProbe;
45-
use super::{
46-
AggregateExec, SubpartitionReorderBuffer, partition_runs, reorder_by_subpartition,
47-
};
45+
use super::{AggregateExec, partition_runs, partition_runs_from_column};
4846
use crate::metrics::{
4947
BaselineMetrics, MetricBuilder, MetricCategory, RecordOutput, SpillMetrics,
5048
};
@@ -56,7 +54,6 @@ struct FinalPartitionRunState {
5654
run_sizes: BTreeMap<usize, usize>,
5755
total_runs_size: usize,
5856
active_partition_size: usize,
59-
subpartition_reorder_buffer: SubpartitionReorderBuffer,
6057
}
6158

6259
impl FinalPartitionRunState {
@@ -66,7 +63,6 @@ impl FinalPartitionRunState {
6663
run_sizes: BTreeMap::new(),
6764
total_runs_size: 0,
6865
active_partition_size: 0,
69-
subpartition_reorder_buffer: SubpartitionReorderBuffer::new(),
7066
}
7167
}
7268

@@ -866,18 +862,17 @@ impl FinalHashAggregateStream {
866862
}
867863

868864
fn stage_partition_runs(&mut self, batch: &RecordBatch) -> Result<Option<usize>> {
869-
if let Some(state) = self.partition_run_state.as_mut()
870-
&& let Some(batch) =
871-
reorder_by_subpartition(batch, &mut state.subpartition_reorder_buffer)?
872-
{
865+
if let Some((batch, runs)) = partition_runs_from_column(batch)? {
873866
let mut offset = 0;
874867
let mut staged_memory = 0;
875-
let num_runs = state.subpartition_reorder_buffer.runs().len();
876-
for run_idx in 0..num_runs {
877-
let run = state.subpartition_reorder_buffer.runs()[run_idx];
868+
for run in runs {
878869
let run_batch = batch.slice(offset, run.len);
879870
offset += run.len;
880-
staged_memory += state.stage_batch(run_batch, run.relative_partition);
871+
staged_memory += self
872+
.partition_run_state
873+
.as_mut()
874+
.map(|state| state.stage_batch(run_batch, run.relative_partition))
875+
.unwrap_or(0);
881876
}
882877
return Ok(Some(staged_memory));
883878
}

datafusion/physical-plan/src/aggregates/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ use itertools::Itertools;
7272
#[cfg(test)]
7373
pub(crate) use partition_runs::set_partition_runs_metadata;
7474
pub(crate) use partition_runs::{
75-
PartitionRun, SubpartitionReorderBuffer, append_subpartition_column, partition_runs,
76-
reorder_by_subpartition, subpartition_schema,
75+
PartitionRun, append_subpartition_column, partition_runs, partition_runs_from_column,
76+
subpartition_schema,
7777
};
7878
use topk::hash_table::is_supported_hash_key_type;
7979
use topk::heap::is_supported_heap_type;

datafusion/physical-plan/src/aggregates/partition_runs.rs

Lines changed: 24 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,12 @@
2121
//! internal contracts used to describe relative aggregate subpartitions passed
2222
//! from repartition to final hash aggregation.
2323
//!
24-
//! Entry points: [`append_subpartition_column`], [`reorder_by_subpartition`],
24+
//! Entry points: [`append_subpartition_column`], [`partition_runs_from_column`],
2525
//! [`set_partition_runs_metadata`] and [`partition_runs`].
2626
2727
use std::sync::Arc;
2828

29-
use arrow::array::{
30-
Array, ArrayRef, MutableArrayData, RecordBatch, RecordBatchOptions, UInt32Array,
31-
make_array,
32-
};
29+
use arrow::array::{Array, ArrayRef, RecordBatch, RecordBatchOptions, UInt32Array};
3330
use arrow::datatypes::{DataType, Field, Schema, SchemaRef};
3431
use datafusion_common::cast::as_uint32_array;
3532
use datafusion_common::{Result, internal_err};
@@ -109,41 +106,11 @@ pub(crate) fn append_subpartition_column(
109106
RecordBatch::try_new_with_options(schema, columns, &options).map_err(Into::into)
110107
}
111108

112-
/// Reusable scratch space for [`reorder_by_subpartition`].
113-
#[derive(Default)]
114-
pub(crate) struct SubpartitionReorderBuffer {
115-
counts: Vec<usize>,
116-
offsets: Vec<usize>,
117-
source_runs: Vec<SourcePartitionRun>,
118-
runs: Vec<PartitionRun>,
119-
}
120-
121-
impl SubpartitionReorderBuffer {
122-
/// Create an empty reorder scratch buffer.
123-
pub(crate) fn new() -> Self {
124-
Self::default()
125-
}
126-
127-
/// Return partition runs produced by the latest reorder.
128-
pub(crate) fn runs(&self) -> &[PartitionRun] {
129-
&self.runs
130-
}
131-
132-
fn clear(&mut self) {
133-
self.counts.clear();
134-
self.offsets.clear();
135-
self.source_runs.clear();
136-
self.runs.clear();
137-
}
138-
}
139-
140-
/// Reorder `batch` by its internal subpartition column and strip that column.
141-
pub(crate) fn reorder_by_subpartition(
109+
/// Read contiguous partition runs from the internal subpartition column and
110+
/// return a batch that omits the internal column without reordering any rows.
111+
pub(crate) fn partition_runs_from_column(
142112
batch: &RecordBatch,
143-
buffer: &mut SubpartitionReorderBuffer,
144-
) -> Result<Option<RecordBatch>> {
145-
buffer.clear();
146-
113+
) -> Result<Option<(RecordBatch, Vec<PartitionRun>)>> {
147114
let Some(subpartition_idx) = subpartition_column_index(batch.schema_ref()) else {
148115
return Ok(None);
149116
};
@@ -155,129 +122,29 @@ pub(crate) fn reorder_by_subpartition(
155122
);
156123
}
157124

158-
let num_rows = batch.num_rows();
159-
if num_rows > u32::MAX as usize {
160-
return internal_err!(
161-
"Hash aggregate batch row count {num_rows} does not fit UInt32"
162-
);
163-
}
164-
165-
for relative_partition in subpartitions.values().iter().copied() {
166-
let relative_partition = relative_partition as usize;
167-
if relative_partition >= buffer.counts.len() {
168-
buffer.counts.resize(relative_partition + 1, 0);
169-
}
170-
buffer.counts[relative_partition] += 1;
171-
}
172-
173-
buffer.offsets.resize(buffer.counts.len(), 0);
174-
let mut output_offset = 0;
175-
for (relative_partition, len) in buffer.counts.iter().copied().enumerate() {
176-
buffer.offsets[relative_partition] = output_offset;
177-
if len != 0 {
178-
buffer
179-
.runs
180-
.push(PartitionRun::new(relative_partition, len)?);
181-
}
182-
output_offset += len;
183-
}
184-
debug_assert_eq!(output_offset, num_rows);
185-
186-
let mut is_identity = true;
187-
let mut values = subpartitions.values().iter().copied().enumerate();
188-
if let Some((run_start, first_partition)) = values.next() {
125+
let mut runs = Vec::new();
126+
let mut values = subpartitions.values().iter().copied();
127+
if let Some(first_partition) = values.next() {
189128
let mut current_partition = first_partition as usize;
190-
let mut current_start = run_start;
191129
let mut current_len = 1;
192-
for (row_idx, relative_partition) in values {
130+
for relative_partition in values {
193131
let relative_partition = relative_partition as usize;
194132
if relative_partition == current_partition {
195133
current_len += 1;
196134
} else {
197-
push_source_run(
198-
buffer,
199-
current_partition,
200-
current_start,
201-
current_len,
202-
&mut is_identity,
203-
);
135+
runs.push(PartitionRun::new(current_partition, current_len)?);
204136
current_partition = relative_partition;
205-
current_start = row_idx;
206137
current_len = 1;
207138
}
208139
}
209-
push_source_run(
210-
buffer,
211-
current_partition,
212-
current_start,
213-
current_len,
214-
&mut is_identity,
215-
);
140+
runs.push(PartitionRun::new(current_partition, current_len)?);
216141
}
217142

218143
let schema = strip_subpartition_schema(batch.schema_ref(), subpartition_idx);
219144
let columns = strip_subpartition_columns(batch, subpartition_idx);
220-
let output = if is_identity {
221-
RecordBatch::try_new(schema, columns)?
222-
} else {
223-
buffer
224-
.source_runs
225-
.sort_unstable_by_key(|run| run.output_start);
226-
let columns =
227-
reorder_columns_by_source_runs(&columns, &buffer.source_runs, num_rows);
228-
let options = RecordBatchOptions::new().with_row_count(Some(num_rows));
229-
RecordBatch::try_new_with_options(schema, columns, &options)?
230-
};
231-
232-
Ok(Some(output))
233-
}
234-
235-
#[derive(Debug, Clone, Copy)]
236-
struct SourcePartitionRun {
237-
start: usize,
238-
len: usize,
239-
output_start: usize,
240-
}
241-
242-
fn push_source_run(
243-
buffer: &mut SubpartitionReorderBuffer,
244-
relative_partition: usize,
245-
start: usize,
246-
len: usize,
247-
is_identity: &mut bool,
248-
) {
249-
let output_start = buffer.offsets[relative_partition];
250-
buffer.offsets[relative_partition] += len;
251-
*is_identity &= output_start == start;
252-
buffer.source_runs.push(SourcePartitionRun {
253-
start,
254-
len,
255-
output_start,
256-
});
257-
}
258-
259-
fn reorder_columns_by_source_runs(
260-
columns: &[ArrayRef],
261-
source_runs: &[SourcePartitionRun],
262-
num_rows: usize,
263-
) -> Vec<ArrayRef> {
264-
columns
265-
.iter()
266-
.map(|column| reorder_array_by_source_runs(column, source_runs, num_rows))
267-
.collect()
268-
}
269-
270-
fn reorder_array_by_source_runs(
271-
array: &ArrayRef,
272-
source_runs: &[SourcePartitionRun],
273-
num_rows: usize,
274-
) -> ArrayRef {
275-
let array_data = array.to_data();
276-
let mut mutable = MutableArrayData::new(vec![&array_data], false, num_rows);
277-
for run in source_runs {
278-
mutable.extend(0, run.start, run.start + run.len);
279-
}
280-
make_array(mutable.freeze())
145+
let options = RecordBatchOptions::new().with_row_count(Some(batch.num_rows()));
146+
let batch = RecordBatch::try_new_with_options(schema, columns, &options)?;
147+
Ok(Some((batch, runs)))
281148
}
282149

283150
/// Return partition runs encoded on `schema`, if present.
@@ -436,7 +303,7 @@ mod tests {
436303
}
437304

438305
#[test]
439-
fn test_reorder_by_subpartition_groups_rows_and_strips_column() -> Result<()> {
306+
fn test_partition_runs_from_column_preserves_row_order() -> Result<()> {
440307
let schema =
441308
Arc::new(Schema::new(vec![Field::new("a", DataType::UInt32, false)]));
442309
let batch = RecordBatch::try_new(
@@ -452,14 +319,17 @@ mod tests {
452319
],
453320
)?;
454321

455-
let mut buffer = SubpartitionReorderBuffer::new();
456-
let batch = reorder_by_subpartition(&batch, &mut buffer)?.unwrap();
322+
let (batch, runs) = partition_runs_from_column(&batch)?.unwrap();
457323
let values = as_uint32_array(batch.column(0).as_ref())?;
458-
assert_eq!(values.values(), &[11, 12, 10, 13]);
324+
assert_eq!(values.values(), &[10, 11, 12, 13]);
459325
assert_eq!(batch.num_columns(), 1);
460326
assert_eq!(
461-
buffer.runs(),
462-
&[PartitionRun::new(0, 2)?, PartitionRun::new(1, 2)?]
327+
runs,
328+
[
329+
PartitionRun::new(1, 1)?,
330+
PartitionRun::new(0, 2)?,
331+
PartitionRun::new(1, 1)?,
332+
]
463333
);
464334
Ok(())
465335
}

0 commit comments

Comments
 (0)