Ungrouped aggregate function pushdown for duckdb#8645
Conversation
|
As we don't have statistics attached to arrays, and we don't use precomputed statistics as Datafusion does, this PR would likely sit idle for some time until these are done, because currently there's no performance improvement compared to the default versions. There will be significant improvement once we add zone map statistics |
Polar Signals Profiling ResultsLatest Run
Previous Runs (6)
Powered by Polar Signals Cloud |
Benchmarks: PolarSignals ProfilingVortex (geomean): 0.947x ➖ How to read Verdict and Engines
datafusion / vortex-file-compressed (0.947x ➖, 1↑ 0↓)
No file size changes detected. |
Benchmarks: TPC-H SF=1 on NVMEVerdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.797x ✅, 22↑ 0↓)
datafusion / vortex-compact (0.801x ✅, 22↑ 0↓)
datafusion / parquet (0.840x ✅, 20↑ 0↓)
datafusion / arrow (0.739x ✅, 22↑ 0↓)
duckdb / vortex-file-compressed (0.838x ✅, 21↑ 0↓)
duckdb / vortex-compact (0.837x ✅, 21↑ 0↓)
duckdb / parquet (0.875x ✅, 12↑ 0↓)
duckdb / duckdb (0.869x ✅, 17↑ 0↓)
File Size Changes (9 files changed, -0.0% overall, 5↑ 4↓)
Totals:
|
Benchmarks: TPC-DS SF=1 on NVMEVerdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.968x ➖, 3↑ 0↓)
datafusion / vortex-compact (0.971x ➖, 3↑ 0↓)
datafusion / parquet (0.970x ➖, 4↑ 1↓)
duckdb / vortex-file-compressed (0.982x ➖, 1↑ 0↓)
duckdb / vortex-compact (0.991x ➖, 0↑ 1↓)
duckdb / parquet (0.988x ➖, 0↑ 0↓)
duckdb / duckdb (0.990x ➖, 2↑ 0↓)
File Size Changes (6 files changed, +0.0% overall, 4↑ 2↓)
Totals:
|
Benchmarks: FineWeb NVMeVerdict: No clear signal (environment too noisy confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.954x ➖, 0↑ 0↓)
datafusion / vortex-compact (0.968x ➖, 1↑ 0↓)
datafusion / parquet (1.046x ➖, 0↑ 5↓)
duckdb / vortex-file-compressed (0.942x ➖, 1↑ 0↓)
duckdb / vortex-compact (0.990x ➖, 0↑ 0↓)
duckdb / parquet (0.965x ➖, 1↑ 0↓)
File Size Changes (1 files changed, -0.0% overall, 0↑ 1↓)
Totals:
|
Benchmarks: Statistical and Population GeneticsVerdict: No clear signal (low confidence) How to read Verdict and Engines
duckdb / vortex-file-compressed (1.037x ➖, 0↑ 0↓)
duckdb / vortex-compact (1.042x ➖, 0↑ 0↓)
duckdb / parquet (1.020x ➖, 0↑ 0↓)
File Size Changes (1 files changed, +0.0% overall, 1↑ 0↓)
Totals:
|
Benchmarks: FineWeb S3Verdict: No clear signal (environment too noisy confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.937x ➖, 1↑ 0↓)
datafusion / vortex-compact (1.050x ➖, 0↑ 1↓)
datafusion / parquet (0.880x ➖, 1↑ 0↓)
duckdb / vortex-file-compressed (0.908x ➖, 0↑ 0↓)
duckdb / vortex-compact (0.912x ➖, 0↑ 0↓)
duckdb / parquet (0.914x ➖, 0↑ 0↓)
|
Benchmarks: Clickbench Sorted on NVMEVerdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (1.011x ➖, 1↑ 1↓)
datafusion / parquet (0.994x ➖, 0↑ 0↓)
duckdb / vortex-file-compressed (1.014x ➖, 0↑ 1↓)
duckdb / parquet (0.990x ➖, 0↑ 0↓)
duckdb / duckdb (1.008x ➖, 0↑ 1↓)
File Size Changes (201 files changed, +0.0% overall, 119↑ 82↓)
Totals:
|
Benchmarks: TPC-H SF=10 on NVMEVerdict: No clear signal (medium confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.954x ➖, 0↑ 0↓)
datafusion / vortex-compact (0.999x ➖, 0↑ 0↓)
datafusion / parquet (0.948x ➖, 0↑ 0↓)
datafusion / arrow (0.890x ✅, 15↑ 0↓)
duckdb / vortex-file-compressed (0.756x ✅, 22↑ 0↓)
duckdb / vortex-compact (0.790x ✅, 22↑ 0↓)
duckdb / parquet (0.933x ➖, 7↑ 0↓)
duckdb / duckdb (0.889x ✅, 12↑ 0↓)
File Size Changes (27 files changed, +0.0% overall, 16↑ 11↓)
Totals:
|
Merging this PR will not alter performance
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | chunked_varbinview_into_canonical[(1000, 10)] |
169.7 µs | 206 µs | -17.61% |
| ❌ | Simulation | encode_varbinview[(10000, 2)] |
994.8 µs | 1,133.4 µs | -12.23% |
| ⚡ | Simulation | bitwise_not_vortex_buffer_mut[128] |
273.6 ns | 215.3 ns | +27.1% |
| ⚡ | Simulation | bitwise_not_vortex_buffer_mut[1024] |
333.9 ns | 275.6 ns | +21.17% |
| ⚡ | Simulation | bitwise_not_vortex_buffer_mut[2048] |
427.8 ns | 369.4 ns | +15.79% |
| ⚡ | Simulation | chunked_varbinview_into_canonical[(100, 100)] |
307.1 µs | 271.9 µs | +12.94% |
| ⚡ | Simulation | chunked_varbinview_opt_canonical_into[(100, 100)] |
340.5 µs | 305.4 µs | +11.51% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing myrrc/temp-agg-fn-pushdown (c5e7653) with develop (11f15a9)
Footnotes
-
42 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
Benchmarks: Clickbench on NVMEVerdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.931x ➖, 8↑ 0↓)
datafusion / parquet (0.941x ➖, 4↑ 0↓)
duckdb / vortex-file-compressed (0.982x ➖, 2↑ 0↓)
duckdb / parquet (0.999x ➖, 0↑ 0↓)
duckdb / duckdb (1.008x ➖, 0↑ 2↓)
File Size Changes (104 files changed, +0.0% overall, 53↑ 51↓)
Totals:
|
Benchmarks: Random AccessVortex (geomean): 1.032x ➖ How to read Verdict and Engines
unknown / unknown (1.056x ➖, 0↑ 3↓)
|
Benchmarks: TPC-H SF=10 on S3Verdict: No clear signal (environment too noisy confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (1.073x ➖, 0↑ 1↓)
datafusion / vortex-compact (1.024x ➖, 0↑ 0↓)
datafusion / parquet (0.953x ➖, 0↑ 0↓)
duckdb / vortex-file-compressed (0.993x ➖, 0↑ 0↓)
duckdb / vortex-compact (0.998x ➖, 0↑ 0↓)
duckdb / parquet (1.036x ➖, 0↑ 0↓)
|
Benchmarks: TPC-H SF=1 on S3Verdict: No clear signal (environment too noisy confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (0.952x ➖, 0↑ 0↓)
datafusion / vortex-compact (0.898x ➖, 2↑ 0↓)
datafusion / parquet (1.172x ➖, 0↑ 7↓)
duckdb / vortex-file-compressed (0.977x ➖, 0↑ 0↓)
duckdb / vortex-compact (0.968x ➖, 0↑ 0↓)
duckdb / parquet (0.991x ➖, 0↑ 0↓)
|
Benchmarks: Appian on NVMEVerdict: No clear signal (low confidence) How to read Verdict and Engines
datafusion / vortex-file-compressed (1.015x ➖, 0↑ 0↓)
datafusion / parquet (0.930x ➖, 1↑ 0↓)
duckdb / vortex-file-compressed (1.082x ➖, 0↑ 1↓)
duckdb / parquet (1.063x ➖, 0↑ 0↓)
duckdb / duckdb (1.110x ❌, 0↑ 6↓)
File Size Changes (3 files changed, -0.0% overall, 1↑ 2↓)
Totals:
|
Benchmarks: CompressionVortex (geomean): 0.993x ➖ How to read Verdict and Engines
unknown / unknown (0.956x ➖, 22↑ 2↓)
|
| let mut accumulators = global_state.partials.lock(); | ||
| let row_count = global_state.row_count.load(Ordering::Acquire) as i64; | ||
| let mut accum_iter = accumulators.iter_mut(); | ||
| for (i, aggregate) in global_state.aggregates.iter().enumerate() { |
There was a problem hiding this comment.
This loop looks a bit manual? How large does accumulators get, should we do sth along the lines of zip / filter here and iter twice?
There was a problem hiding this comment.
We support 6 distinct aggregations but there may be many more columns for them.
There was a problem hiding this comment.
Saying we could split it into 2 loops and make this simpler?
afd1cd5 to
2b742eb
Compare
2b742eb to
10c6a50
Compare
25b9ddb to
06cb841
Compare
06cb841 to
f90f65d
Compare
| let mut accumulators = global_state.partials.lock(); | ||
| let row_count = global_state.row_count.load(Ordering::Acquire) as i64; | ||
| let mut accum_iter = accumulators.iter_mut(); | ||
| for (i, aggregate) in global_state.aggregates.iter().enumerate() { |
There was a problem hiding this comment.
Saying we could split it into 2 loops and make this simpler?
f90f65d to
9d108d7
Compare
Signed-off-by: Mikhail Kot <mikhail@spiraldb.com>
9d108d7 to
c5e7653
Compare
This PR adds support for pushing down ungropped aggregates min,max,sum,avg,mean,first,any_value,count(col), and count(*).
It consists of roughly 3 parts:
I've tried the implementation with CountStart being a separate accumulator, and it is much more complex than current one, because you can't provide a real column to accumulate on in select(), and you need to create a dummy one. So, we pay the cost of a separate atomic and a count_star positions vector, but our expression projection handling stays simple.
We also reject count(*) if it's the only aggregate because duckdb calculating it natively (just using chunk lengths) is faster than our accumulator pipeline
On the API input note, nearly all aggregate functions except for count_star() in duckdb have one non-const argument so input is a pair of a column and an expression.
Depends on #8649