Skip to content

Commit 85b64a7

Browse files
Rachel IntRachel Int
authored andcommitted
perf: test a 2 MiB partial aggregation flush threshold
1 parent 88d36d4 commit 85b64a7

1 file changed

Lines changed: 23 additions & 4 deletions

File tree

‎datafusion/physical-plan/src/aggregates/hash_stream.rs‎

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,9 @@ fn starts_buckets(groups: usize, rows: usize, threshold: usize) -> bool {
246246
&& groups as f64 >= BARELY_REDUCING * rows as f64)
247247
}
248248

249+
/// Additional Partial flush trigger in allocated bytes, checked after a batch.
250+
const PARTIAL_FLUSH_BYTES: usize = 2 * 1024 * 1024;
251+
249252
/// Number of flushed groups whose hashes are kept to detect recurring groups.
250253
const FLUSH_SAMPLE_SIZE: usize = 1024;
251254

@@ -296,8 +299,9 @@ struct PartialTableFlush {
296299
}
297300

298301
impl PartialTableFlush {
299-
fn should_flush(&self, num_groups: usize) -> bool {
300-
!self.disabled && num_groups >= self.threshold
302+
fn should_flush(&self, num_groups: usize, allocated_bytes: usize) -> bool {
303+
!self.disabled
304+
&& (num_groups >= self.threshold || allocated_bytes >= PARTIAL_FLUSH_BYTES)
301305
}
302306

303307
/// Records the flush of `state`, the emitted groups and their states.
@@ -602,7 +606,8 @@ impl PartialHashAggregateStream {
602606
// -------------------------------------------------
603607
// Step 4: Larger-than-memory execution (early emit)
604608
// -------------------------------------------------
605-
let resize_result = self.reservation.try_resize(hash_table.memory_size());
609+
let allocated_bytes = hash_table.memory_size();
610+
let resize_result = self.reservation.try_resize(allocated_bytes);
606611
match resize_result {
607612
Ok(()) => {}
608613
Err(DataFusionError::ResourcesExhausted(_)) => {
@@ -616,7 +621,7 @@ impl PartialHashAggregateStream {
616621
// `PartialTableFlush`)
617622
// -----------------------------------------------------------
618623
let table_full = self.table_flush.as_ref().is_some_and(|table_flush| {
619-
table_flush.should_flush(hash_table.building_group_count())
624+
table_flush.should_flush(hash_table.building_group_count(), allocated_bytes)
620625
});
621626
if table_full {
622627
return Ok(HandleInputResult::TableFull);
@@ -1710,6 +1715,20 @@ mod tests {
17101715
Ok(())
17111716
}
17121717

1718+
#[tokio::test]
1719+
async fn partial_hash_aggregate_flushes_at_byte_threshold() -> Result<()> {
1720+
// Stay below the group threshold so only the byte threshold can flush.
1721+
let keys: Vec<i32> = (0..300_000).map(|row| row / 3).collect();
1722+
let (expected, _, _) = run_partial_hash_aggregate(keys.clone(), 0).await?;
1723+
let (counts, _, flushes) = run_partial_hash_aggregate(keys, 262_144).await?;
1724+
1725+
assert_eq!(counts, expected);
1726+
assert_eq!(counts.len(), 100_000);
1727+
assert!(counts.values().all(|&count| count == 3));
1728+
assert!(flushes > 0, "the byte threshold triggered a flush");
1729+
Ok(())
1730+
}
1731+
17131732
#[tokio::test]
17141733
async fn partial_hash_aggregate_stops_flushing_groups_that_recur() -> Result<()> {
17151734
// The same 30000 keys come around five times: every flushed group

0 commit comments

Comments
 (0)