Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize "ORDER BY + LIMIT" queries for speed / memory with special TopK operator #7721

Merged
merged 36 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
524af05
Prototype TopK operator
alamb Aug 9, 2023
d4c09f2
Avoid use of Row
alamb Aug 14, 2023
6c85247
Merge remote-tracking branch 'apache/main' into alamb/topk
alamb Aug 18, 2023
948c1a2
start working on compaction
alamb Aug 22, 2023
354d687
checkpoint
alamb Aug 22, 2023
afea7d3
update
alamb Aug 22, 2023
69b86ab
checkpoint
alamb Aug 23, 2023
c8b415c
fmt
alamb Aug 23, 2023
0337e31
Fix compaction
alamb Aug 23, 2023
db196fb
add location for re-encoding
alamb Aug 24, 2023
f123075
Start sketching dictionary interleave
alamb Aug 24, 2023
157379a
checkpoint
alamb Aug 24, 2023
682127a
initial specialized dictionary
alamb Aug 24, 2023
a1ea62e
finish initial special interleave
alamb Aug 24, 2023
5e65130
Complete dictionary order
alamb Aug 24, 2023
7f29366
Merge
Dandandan Oct 2, 2023
4a30c4c
Merge
Dandandan Oct 2, 2023
78163bd
Merge remote-tracking branch 'upstream/main' into topk
Dandandan Oct 2, 2023
d9c596f
fmt
Dandandan Oct 2, 2023
c0f89c1
Cleanup
Dandandan Oct 2, 2023
466d4b6
Fix test
Dandandan Oct 2, 2023
33065ad
Cleanup
Dandandan Oct 2, 2023
e31718e
Make test deterministic
Dandandan Oct 2, 2023
40ef448
Clippy, doctest
Dandandan Oct 2, 2023
c373ce3
Use into_sorted_vec
Dandandan Oct 3, 2023
bd72ad8
Fix nondeterministic tests
Dandandan Oct 3, 2023
84ffae8
Update cargo.lock
Dandandan Oct 3, 2023
21ea10f
Merge
Dandandan Oct 3, 2023
592b10e
Update datafusion/physical-plan/src/topk/mod.rs
Dandandan Oct 3, 2023
47ee199
Update datafusion/physical-plan/src/topk/mod.rs
Dandandan Oct 3, 2023
2c33637
Update datafusion/physical-plan/src/topk/mod.rs
Dandandan Oct 4, 2023
c9121cc
Update datafusion/physical-plan/src/topk/mod.rs
Dandandan Oct 4, 2023
0dc3488
Add / update some comments
Dandandan Oct 4, 2023
0470306
Rename test file
Dandandan Oct 4, 2023
0c59fe1
Rename table as well
Dandandan Oct 4, 2023
6bb299b
Update datafusion/sqllogictest/test_files/topk.slt
Dandandan Oct 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions datafusion-cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions datafusion/physical-plan/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

//! Traits for physical query plan, supporting parallel execution for partitioned relations.

mod topk;
mod visitor;
pub use self::metrics::Metric;
use self::metrics::MetricsSet;
Expand All @@ -26,6 +27,7 @@ use self::{
pub use datafusion_common::{internal_err, ColumnStatistics, Statistics};
use datafusion_common::{plan_err, Result};
use datafusion_physical_expr::PhysicalSortExpr;
pub use topk::TopK;
pub use visitor::{accept, visit_execution_plan, ExecutionPlanVisitor};

use arrow::datatypes::SchemaRef;
Expand Down
79 changes: 55 additions & 24 deletions datafusion/physical-plan/src/sorts/sort.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use crate::metrics::{
};
use crate::sorts::merge::streaming_merge;
use crate::stream::{RecordBatchReceiverStream, RecordBatchStreamAdapter};
use crate::topk::TopK;
use crate::{
DisplayAs, DisplayFormatType, Distribution, EmptyRecordBatchStream, ExecutionPlan,
Partitioning, SendableRecordBatchStream, Statistics,
Expand Down Expand Up @@ -765,7 +766,12 @@ impl DisplayAs for SortExec {
let expr: Vec<String> = self.expr.iter().map(|e| e.to_string()).collect();
match self.fetch {
Some(fetch) => {
write!(f, "SortExec: fetch={fetch}, expr=[{}]", expr.join(","))
write!(
f,
// TODO should this say topk?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if we would like to do this? I think there are some other ExecutionPlan nodes that have the algorithm depend on one of the parameters (for example: HashAggregate modes) .

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in general it would be good to be able to tell what operator was going to be used from looking at the plan. However, I think we can do so as a follow on PR -- I can file a ticket.

Copy link
Contributor

@alamb alamb Oct 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#7750 tracks this work

"SortExec: fetch={fetch}, expr=[{}]",
expr.join(",")
)
}
None => write!(f, "SortExec: expr=[{}]", expr.join(",")),
}
Expand Down Expand Up @@ -853,29 +859,54 @@ impl ExecutionPlan for SortExec {

trace!("End SortExec's input.execute for partition: {}", partition);

let mut sorter = ExternalSorter::new(
partition,
input.schema(),
self.expr.clone(),
context.session_config().batch_size(),
self.fetch,
execution_options.sort_spill_reservation_bytes,
execution_options.sort_in_place_threshold_bytes,
&self.metrics_set,
context.runtime_env(),
);
if let Some(fetch) = self.fetch.as_ref() {
let mut topk = TopK::try_new(
partition,
input.schema(),
self.expr.clone(),
*fetch,
context.session_config().batch_size(),
context.runtime_env(),
&self.metrics_set,
partition,
)?;

Ok(Box::pin(RecordBatchStreamAdapter::new(
self.schema(),
futures::stream::once(async move {
while let Some(batch) = input.next().await {
let batch = batch?;
topk.insert_batch(batch)?;
}
topk.emit()
})
.try_flatten(),
)))
} else {
let mut sorter = ExternalSorter::new(
partition,
input.schema(),
self.expr.clone(),
context.session_config().batch_size(),
self.fetch,
execution_options.sort_spill_reservation_bytes,
execution_options.sort_in_place_threshold_bytes,
&self.metrics_set,
context.runtime_env(),
);

Ok(Box::pin(RecordBatchStreamAdapter::new(
self.schema(),
futures::stream::once(async move {
while let Some(batch) = input.next().await {
let batch = batch?;
sorter.insert_batch(batch).await?;
}
sorter.sort()
})
.try_flatten(),
)))
Ok(Box::pin(RecordBatchStreamAdapter::new(
self.schema(),
futures::stream::once(async move {
while let Some(batch) = input.next().await {
let batch = batch?;
sorter.insert_batch(batch).await?;
}
sorter.sort()
})
.try_flatten(),
)))
}
}

fn metrics(&self) -> Option<MetricsSet> {
Expand Down Expand Up @@ -1043,7 +1074,7 @@ mod tests {
assert_eq!(result.len(), 1);

let metrics = sort_exec.metrics().unwrap();
let did_it_spill = metrics.spill_count().unwrap() > 0;
let did_it_spill = metrics.spill_count().unwrap_or(0) > 0;
assert_eq!(did_it_spill, expect_spillage, "with fetch: {fetch:?}");
}
Ok(())
Expand Down
Loading