Skip to content

fix: bound parallel batch sorting in SortExec - #26242

Open
geoffreyclaude wants to merge 12 commits into
apache:mainfrom
geoffreyclaude:codex/bound-sort-preparation
Open

geoffreyclaude wants to merge 12 commits into
apache:mainfrom
geoffreyclaude:codex/bound-sort-preparation

Conversation

@geoffreyclaude

@geoffreyclaude geoffreyclaude commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Addresses the unbounded local-sort task population discussed in #23174.

Rationale for this change

SortExec sorts pieces of buffered input into independent runs, then merges them. On multithreaded Tokio runtimes, it currently creates one buffered producer task per run.

A one-batch channel limits each producer's output, but the number of tasks still grows with the number of runs. With many input batches, this task buildup can stall unrelated queries on the same runtime. Large inputs can trigger this buildup even with normal-sized batches.

What changes are included in this PR?

Use bounded preparation for both final in-memory sorting and sorting before a spill:

  1. Wait before spawning. Preparations acquire a shared permit before creating a Tokio task. All partitions of one SortExec share a budget capped by both the runtime's worker count and DataFusion's estimate of available CPU parallelism. Each partition also bounds its pending preparations.
  2. Sort, return, exit. Each job sorts one run, reserves its complete output, and returns a stream holding the batches and their reservations. The job then exits and releases its permit.
  3. Merge the prepared streams. Output stays reserved until its batches are read. The merge receives the streams in their original run order, keeping its output independent of task completion order.

Memory handoff: during merging, one-batch lookahead returns workspace for the merge's batches and cursors. Each run may hold one prefetched batch whose sort-side reservation has already been released, preserving the previous buffering bound.

Cancellation aborts outstanding jobs. Resetting execution state gives the new plan a fresh concurrency limit.

Single-batch and concatenate-and-sort paths continue inline, as do current-thread runtimes and execution outside Tokio. TopK is unchanged. SortExec rustdoc describes the limit and its lifetime.

What is the testing strategy for this PR?

  • Memory and output: a competing-consumer regression checks that prepared output stays reserved while released scratch space can be reused. Tight-workspace tests check complete merging and cleanup when unread output is dropped.
  • Task lifetime and timing: focused tests cover permit ownership, cancellation, errors, the shared limit and its reset, and compute recording before output consumption.
  • SQL benchmarks: at 416c2d6, the benchmark bot classified all 11 queries in each of sort_tpch (SF1) and sort_tpch10 (SF10) as "no change". These Parquet-based suites use the default 8,192-row batch target.

A local TPC-H SF1 test uses 32-row batches to stress task growth with a modest dataset. With 4 Tokio workers and 4 partitions, two runs per revision gave:

Measurement Upstream base PR head
Sampled peak live Tokio tasks ~187k 9
Slowest SELECT 1 probe 2.83–2.89 s 155–165 ms

Are there any user-facing changes?

Sorts with many runs have a bounded population of batch-sort jobs across partitions. Public configuration and API signatures are unchanged.

SortExec's elapsed_compute now includes local batch sorting and its memory accounting on both inline and spawned paths, while excluding waits for a permit or another task. EXPLAIN ANALYZE can therefore report larger values.

Durations from concurrent sort jobs are added together and can exceed the query's wall-clock duration.

This PR was prepared with AI assistance.

@github-actions github-actions Bot added the physical-plan Changes to the physical-plan crate label Oct 12, 2026
@codecov-commenter

codecov-commenter commented Oct 12, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.98305% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.91%. Comparing base (fbf7737) to head (cc90f9e).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
datafusion/physical-plan/src/sorts/sort.rs 88.98% 3 Missing and 23 partials ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main   #26242    +/-   ##
========================================
  Coverage   82.91%   82.91%            
========================================
  Files        1155     1155            
  Lines      458091   458301   +210     
  Branches   458091   458301   +210     
========================================
+ Hits       379823   380008   +185     
  Misses      55229    55229            
- Partials    23039    23064    +25     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@geoffreyclaude
geoffreyclaude force-pushed the codex/bound-sort-preparation branch 13 times, most recently from e2e4c5e to 9d0897a Compare October 13, 2026 15:34
@github-actions github-actions Bot added the optimizer Optimizer rules label Oct 13, 2026
@geoffreyclaude
geoffreyclaude force-pushed the codex/bound-sort-preparation branch 4 times, most recently from 67093a0 to 1eacb12 Compare October 14, 2026 09:49
@github-actions github-actions Bot removed the optimizer Optimizer rules label Oct 14, 2026
@geoffreyclaude
geoffreyclaude marked this pull request as ready for review October 14, 2026 14:07
.map_err(|e| internal_datafusion_err!("Sort concurrency closed: {e}"))?;
SpawnedTask::spawn(async move {
// Match the buffered producer's first-batch handoff so spill
// workspace can be reused while the remaining runs are sorted.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think this comment adds much value.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

indeed, removed.

// current batch, returning workspace before the merge
// reserves memory for that batch and its cursor.
let next = stream.next().await;
Some((batch, (next, stream)))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Haven't read the consumer side of this stream yet but here's one observation -

Since the batch is unaccounted, why not re-reserve the memory here in self.merge_pool?

  • In total across all permits, there's batch_size * semaphore size, so that's a lot of unaccounted memory right?
  • The consumed stream returns all the reserved memory (to the workspace or otherwise) before yielding a batch

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Preparation now keeps all sorted output reserved until merging starts. During merging, the one-batch lookahead still temporarily releases the prefetched batch’s reservation so the merge can reuse that workspace.

#[derive(Debug)]
struct SortConcurrency {
/// Bounds preparation futures buffered by each partition.
limit: usize,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm I guess you need this bc there's no API for total semaphore size?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, you can only easily get the still available permits, but we want the total size. Renamed the field to per_partition_limit to clarify its purpose.

.prepare_sort_runs(runs, concurrency)
.with_elapsed_compute(elapsed_compute.clone())
.await?;
timer.restart();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

So pausing the timer actually fails to capture the time spent in the sort_batch_stream stream which calls sort_batch_chunked, which is elapsed compute we want to capture. Maybe pass the timer as a param? Or something better, a timer param feels like a smell.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed: sort_run now measures sorting and memory accounting on the task doing the work, for both inline and spawned execution. Scheduling waits remain excluded.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh neat, with_elapsed_compute is quite handy

.sum::<usize>()
- input_bytes;
let first_bytes = get_record_batch_memory_size(&sorted_batches[0]);
assert!(loan > first_bytes);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you comment why it's bigger? I guess it's because some 4096 size buffers are re-used but not filled?

@geoffreyclaude geoffreyclaude Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Each sorted StringView chunk retains the shared string buffers, and summing the chunks' memory sizes counts those buffers repeatedly. I’ve added a comment explaining this.

Related: #25820 fixes shared-buffer counting for sort inputs, but explicitly leaves StringView accounting for follow-up work (I ran into a conflict with that PR when rebasing over the latest main.)

assert!(alive <= 1, "{alive} sort tasks remain before merging");
assert_eq!(
sorter.merge_pool.borrow(usize::MAX).size(),
workspace - 2 * (loan - first_bytes)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Adding comments around the math would make it easier to reason about

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should hopefully be a tiny bit clearer now!

@geoffreyclaude
geoffreyclaude force-pushed the codex/bound-sort-preparation branch 2 times, most recently from 4ba6d18 to 96f3f8a Compare October 14, 2026 22:25
@github-actions github-actions Bot added the auto detected api change Auto detected API change label Oct 14, 2026
@geoffreyclaude
geoffreyclaude force-pushed the codex/bound-sort-preparation branch from 96f3f8a to d1a59c1 Compare October 15, 2026 06:47
@geoffreyclaude

Copy link
Copy Markdown
Contributor Author

run benchmark sort_tpch sort_tpch10

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c6186268738-3434-bwhsq 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing codex/bound-sort-preparation (d1a59c1) to e732212 (merge-base) diff

Run configuration
run benchmark sort_tpch

Results will be posted here when complete


File an issue against this benchmark runner

@geoffreyclaude
geoffreyclaude force-pushed the codex/bound-sort-preparation branch from 5d19187 to cc90f9e Compare October 19, 2026 15:14
@gabotechs
gabotechs self-requested a review October 19, 2026 15:37
@comphead

Copy link
Copy Markdown
Contributor

Thanks @geoffreyclaude I'm doing first automated review and then check manually

@comphead comphead left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the fix. The permit-before-spawn design and the ordered reassembly look sound to me. Notes below, most important first.

  • Coverage of the new path. sort_fuzz.rs and spilling_fuzz_in_memory_constrained_env.rs use #[tokio::test] (current-thread), so they only run the inline path. The default sort_in_place_threshold_bytes is 1 MiB, so SLT inputs mostly collapse into one run and skip the spawned path too. The spawned path is then covered only by the unit and spill tests here. A multi-thread variant of the sort fuzz with a small threshold, asserting the same output as the inline path, would cover run counts, batch sizes and spill together.
  • Benchmark evidence. The description gives the before numbers for the 32-row reproducer but no after numbers (alive task count, SELECT 1 probe latency). The bot runs were on d1a59c1, which is earlier than the current head cc90f9e, and TPC-H sort inputs are not fragmented, so "no change" says little about the changed path. Please post the reproducer numbers on the current head, or add a small-batch case to an existing sort bench.
  • Budget scope. The OnceLock lives on the plan and is shared by every clone, so two concurrent executions of one plan instance share the permits, and a re-run on another runtime keeps the first runtime's worker count until reset_state. Probably acceptable, but worth one sentence in the SortExec rustdoc if intended.

/// reservation when that batch is pulled. Reading one batch ahead returns
/// extra spill workspace for the merge's current batch and cursor.
/// Each run can hold one prefetched batch whose reservation is already released.
fn with_lookahead(stream: SendableRecordBatchStream) -> SendableRecordBatchStream {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

with_lookahead looks equivalent to stream.map(std::future::ready).buffered(2) wrapped in RecordBatchStreamAdapter. Buffered fills its queue to 2 before yielding, so each yielded batch has exactly one successor already pulled, same as the unfold state here. That would drop the hand-written state machine. I have not run it, so please confirm the reservation release order in test_sort_merge_lookahead_returns_cursor_workspace.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right: this was premature optimization (I was concerned about extra per-batch allocations and bookkeeping). I've switched to buffered(2): it's much clearer, and the query-level benchmarks showed no performance difference. I also ran test_sort_merge_lookahead_returns_cursor_workspace and can confirm it passes with buffered(2).

concurrency: &SortConcurrency,
) -> Result<Vec<SendableRecordBatchStream>> {
let mut streams: Vec<_> = (0..runs.len()).map(|_| None).collect();
let (sizes, shared_input_size) = self.sort_run_sizes(&runs)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two small points. (1) These three lines (sort_run_sizes, shared-input split, per-run split) are repeated in sort_run_streams. A shared helper returning the per-run reservations plus the optional shared one would keep the two paths from drifting. (2) prepare_sort_runs takes &mut self but only calls reservation.split(&self), like sort_run_streams(&self). &self is enough if the future stays Send (not checked).

@geoffreyclaude geoffreyclaude Oct 21, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

(1) I’d prefer keeping these few lines explicit, following @jayshrivastava's earlier feedback. A shared helper would add ownership complexity to preserve lazy splitting.

(2) Unfortunately, switching to &self would make the future non-Send because ExternalSorter isn’t Sync.

}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn sort_execution_initializes_and_resets_concurrency() -> Result<()> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This test collects but never checks the output. Since the claim is that the spawned path matches the inline one, please assert the collected rows against the same plan run on a current-thread runtime. Use a payload column so equal keys are distinguishable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added distinct payloads and checked each partition against explicit expected rows, including equal-key ordering, in b87cde6. I used explicit expectations rather than a second runtime to keep the test simple.


/// Prepared runs retain their workspace loans until merging starts. Looking
/// ahead during the merge returns enough workspace for its batch and cursors.
async fn check_sort_preparation_workspace(consume_output: bool) -> Result<()> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The description says the merge receives runs in original order so equal-key ties are preserved, but nothing pins that. Both runs here are the same batch and only have the key column, so any run order gives identical output. A test with a payload column, where the earlier run finishes last (for example a much larger first run), would cover the try_buffer_unordered plus streams[index] reassembly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added sort_preparation_restores_run_order in 00cf3cd. It supplies completed runs in reverse order and checks their original order is restored, with distinct payloads for equal keys. This avoids relying on batch sizes or timing.

@geoffreyclaude
geoffreyclaude force-pushed the codex/bound-sort-preparation branch from 29b0afc to b87cde6 Compare October 21, 2026 08:59
@geoffreyclaude
geoffreyclaude force-pushed the codex/bound-sort-preparation branch from 9e14937 to 00cf3cd Compare October 21, 2026 09:48
Keep the randomized sort configurations and compare each non-TopK query against forced inline and bounded parallel sorting, preserving small input batches in both arms.

Compare complete results in row order after concatenating batches. Every projected column is an ORDER BY key, so equal keys have identical output. Retain the current-thread determinism test.

This changes internal test coverage only; no public documentation update is needed.
@github-actions github-actions Bot added the core Core DataFusion crate label Oct 21, 2026
@geoffreyclaude

Copy link
Copy Markdown
Contributor Author

run benchmark sort_tpch sort_tpch10

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c6315642560-3714-s79zc 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing codex/bound-sort-preparation (416c2d6) to fbf7737 (merge-base) diff

Run configuration
run benchmark sort_tpch10

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c6315642560-3713-mmrf7 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing codex/bound-sort-preparation (416c2d6) to fbf7737 (merge-base) diff

Run configuration
run benchmark sort_tpch

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing codex/bound-sort-preparation (416c2d6) to fbf7737 (merge-base) diff

Run configuration
run benchmark sort_tpch
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and codex_bound-sort-preparation
--------------------
Benchmark sort_tpch1.json
--------------------
┏━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query ┃      HEAD ┃ codex_bound-sort-preparation ┃    Change ┃
┡━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Q1    │ 111.89 ms │                    112.65 ms │ no change │
│ Q2    │  98.85 ms │                     99.34 ms │ no change │
│ Q3    │ 665.10 ms │                    655.88 ms │ no change │
│ Q4    │ 161.68 ms │                    161.27 ms │ no change │
│ Q5    │ 235.61 ms │                    236.65 ms │ no change │
│ Q6    │ 249.28 ms │                    249.84 ms │ no change │
│ Q7    │ 422.15 ms │                    423.68 ms │ no change │
│ Q8    │ 302.69 ms │                    297.73 ms │ no change │
│ Q9    │ 318.87 ms │                    313.39 ms │ no change │
│ Q10   │ 481.15 ms │                    472.97 ms │ no change │
│ Q11   │ 249.09 ms │                    248.55 ms │ no change │
└───────┴───────────┴──────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Benchmark Summary                           ┃           ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Total Time (HEAD)                           │ 3296.36ms │
│ Total Time (codex_bound-sort-preparation)   │ 3271.95ms │
│ Average Time (HEAD)                         │  299.67ms │
│ Average Time (codex_bound-sort-preparation) │  297.45ms │
│ Queries Faster                              │         0 │
│ Queries Slower                              │         0 │
│ Queries with No Change                      │        11 │
│ Queries with Failure                        │         0 │
└─────────────────────────────────────────────┴───────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and codex_bound-sort-preparation
--------------------
Benchmark sort_tpch1.json
--------------------
┏━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query ┃                               HEAD ┃       codex_bound-sort-preparation ┃    Change ┃
┡━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Q1    │  111.89 / 112.53 ±0.77 / 113.98 ms │  112.65 / 113.44 ±1.06 / 115.50 ms │ no change │
│ Q2    │   98.85 / 100.05 ±1.26 / 102.50 ms │   99.34 / 100.68 ±1.49 / 103.47 ms │ no change │
│ Q3    │  665.10 / 671.18 ±5.45 / 678.79 ms │  655.88 / 660.75 ±3.37 / 665.98 ms │ no change │
│ Q4    │  161.68 / 162.28 ±0.43 / 162.73 ms │  161.27 / 162.22 ±0.77 / 162.97 ms │ no change │
│ Q5    │  235.61 / 236.91 ±1.07 / 238.21 ms │  236.65 / 238.12 ±1.58 / 241.06 ms │ no change │
│ Q6    │  249.28 / 252.37 ±2.32 / 256.28 ms │  249.84 / 251.16 ±0.75 / 251.91 ms │ no change │
│ Q7    │  422.15 / 424.62 ±2.23 / 427.59 ms │  423.68 / 424.85 ±0.95 / 426.25 ms │ no change │
│ Q8    │  302.69 / 307.90 ±7.94 / 323.51 ms │  297.73 / 303.78 ±6.83 / 316.37 ms │ no change │
│ Q9    │ 318.87 / 327.73 ±13.17 / 353.80 ms │  313.39 / 317.19 ±3.47 / 322.41 ms │ no change │
│ Q10   │  481.15 / 491.13 ±7.72 / 501.60 ms │ 472.97 / 487.13 ±11.66 / 506.75 ms │ no change │
│ Q11   │  249.09 / 258.92 ±9.87 / 273.43 ms │ 248.55 / 267.83 ±19.30 / 299.62 ms │ no change │
└───────┴────────────────────────────────────┴────────────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Benchmark Summary                           ┃           ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Total Time (HEAD)                           │ 3345.62ms │
│ Total Time (codex_bound-sort-preparation)   │ 3327.15ms │
│ Average Time (HEAD)                         │  304.15ms │
│ Average Time (codex_bound-sort-preparation) │  302.47ms │
│ Queries Faster                              │         0 │
│ Queries Slower                              │         0 │
│ Queries with No Change                      │        11 │
│ Queries with Failure                        │         0 │
└─────────────────────────────────────────────┴───────────┘

Resource Usage

sort_tpch — base (merge-base)

Metric Value
Wall time 20.0s
Peak memory 2.5 GiB
Avg memory 1.1 GiB
CPU user 63.8s
CPU sys 3.1s
Peak spill 0 B

sort_tpch — branch

Metric Value
Wall time 20.0s
Peak memory 2.2 GiB
Avg memory 1.0 GiB
CPU user 61.2s
CPU sys 3.0s
Peak spill 0 B

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing codex/bound-sort-preparation (416c2d6) to fbf7737 (merge-base) diff

Run configuration
run benchmark sort_tpch10
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and codex_bound-sort-preparation
--------------------
Benchmark sort_tpch10.json
--------------------
┏━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query ┃       HEAD ┃ codex_bound-sort-preparation ┃    Change ┃
┡━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Q1    │ 1405.06 ms │                   1417.67 ms │ no change │
│ Q2    │ 1292.96 ms │                   1268.38 ms │ no change │
│ Q3    │ 8156.80 ms │                   8023.25 ms │ no change │
│ Q4    │ 2178.89 ms │                   2165.64 ms │ no change │
│ Q5    │ 2354.00 ms │                   2365.22 ms │ no change │
│ Q6    │ 2478.03 ms │                   2509.93 ms │ no change │
│ Q7    │ 4856.77 ms │                   4791.39 ms │ no change │
│ Q8    │ 3432.06 ms │                   3419.81 ms │ no change │
│ Q9    │ 3722.85 ms │                   3722.70 ms │ no change │
│ Q10   │ 5849.88 ms │                   5797.79 ms │ no change │
│ Q11   │ 3380.53 ms │                   3446.31 ms │ no change │
└───────┴────────────┴──────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Benchmark Summary                           ┃            ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ Total Time (HEAD)                           │ 39107.84ms │
│ Total Time (codex_bound-sort-preparation)   │ 38928.08ms │
│ Average Time (HEAD)                         │  3555.26ms │
│ Average Time (codex_bound-sort-preparation) │  3538.92ms │
│ Queries Faster                              │          0 │
│ Queries Slower                              │          0 │
│ Queries with No Change                      │         11 │
│ Queries with Failure                        │          0 │
└─────────────────────────────────────────────┴────────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and codex_bound-sort-preparation
--------------------
Benchmark sort_tpch10.json
--------------------
┏━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query ┃                                   HEAD ┃           codex_bound-sort-preparation ┃    Change ┃
┡━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Q1    │   1405.06 / 1410.06 ±6.57 / 1422.58 ms │   1417.67 / 1420.13 ±3.24 / 1426.18 ms │ no change │
│ Q2    │  1292.96 / 1334.32 ±27.33 / 1366.85 ms │  1268.38 / 1308.75 ±24.15 / 1335.12 ms │ no change │
│ Q3    │  8156.80 / 8185.80 ±24.84 / 8228.85 ms │ 8023.25 / 8142.07 ±117.46 / 8341.10 ms │ no change │
│ Q4    │  2178.89 / 2215.63 ±31.31 / 2272.39 ms │  2165.64 / 2188.94 ±15.70 / 2208.65 ms │ no change │
│ Q5    │  2354.00 / 2390.06 ±25.69 / 2431.59 ms │  2365.22 / 2382.29 ±17.57 / 2415.11 ms │ no change │
│ Q6    │  2478.03 / 2519.34 ±21.89 / 2539.67 ms │   2509.93 / 2520.70 ±9.75 / 2534.93 ms │ no change │
│ Q7    │  4856.77 / 4884.93 ±27.56 / 4931.04 ms │  4791.39 / 4818.88 ±23.47 / 4853.52 ms │ no change │
│ Q8    │  3432.06 / 3569.39 ±80.91 / 3672.15 ms │  3419.81 / 3463.08 ±26.41 / 3497.78 ms │ no change │
│ Q9    │  3722.85 / 3802.56 ±43.79 / 3850.64 ms │  3722.70 / 3776.28 ±36.05 / 3824.99 ms │ no change │
│ Q10   │ 5849.88 / 6150.55 ±155.92 / 6273.57 ms │ 5797.79 / 6151.59 ±185.70 / 6310.31 ms │ no change │
│ Q11   │  3380.53 / 3413.34 ±20.81 / 3443.07 ms │  3446.31 / 3469.67 ±28.29 / 3522.08 ms │ no change │
└───────┴────────────────────────────────────────┴────────────────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Benchmark Summary                           ┃            ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ Total Time (HEAD)                           │ 39875.99ms │
│ Total Time (codex_bound-sort-preparation)   │ 39642.39ms │
│ Average Time (HEAD)                         │  3625.09ms │
│ Average Time (codex_bound-sort-preparation) │  3603.85ms │
│ Queries Faster                              │          0 │
│ Queries Slower                              │          0 │
│ Queries with No Change                      │         11 │
│ Queries with Failure                        │          0 │
└─────────────────────────────────────────────┴────────────┘

Resource Usage

sort_tpch10 — base (merge-base)

Metric Value
Wall time 205.0s
Peak memory 19.1 GiB
Avg memory 7.0 GiB
CPU user 816.6s
CPU sys 70.2s
Peak spill 0 B

sort_tpch10 — branch

Metric Value
Wall time 200.0s
Peak memory 18.9 GiB
Avg memory 7.3 GiB
CPU user 820.1s
CPU sys 71.3s
Peak spill 0 B

File an issue against this benchmark runner

@geoffreyclaude

Copy link
Copy Markdown
Contributor Author

Thanks for the fix. The permit-before-spawn design and the ordered reassembly look sound to me. Notes below, most important first.

  • Coverage of the new path. sort_fuzz.rs and spilling_fuzz_in_memory_constrained_env.rs use #[tokio::test] (current-thread), so they only run the inline path. The default sort_in_place_threshold_bytes is 1 MiB, so SLT inputs mostly collapse into one run and skip the spawned path too. The spawned path is then covered only by the unit and spill tests here. A multi-thread variant of the sort fuzz with a small threshold, asserting the same output as the inline path, would cover run counts, batch sizes and spill together.
  • Benchmark evidence. The description gives the before numbers for the 32-row reproducer but no after numbers (alive task count, SELECT 1 probe latency). The bot runs were on d1a59c1, which is earlier than the current head cc90f9e, and TPC-H sort inputs are not fragmented, so "no change" says little about the changed path. Please post the reproducer numbers on the current head, or add a small-batch case to an existing sort bench.
  • Budget scope. The OnceLock lives on the plan and is shared by every clone, so two concurrent executions of one plan instance share the permits, and a re-run on another runtime keeps the first runtime's worker count until reset_state. Probably acceptable, but worth one sentence in the SortExec rustdoc if intended.

@comphead Thanks for the detailed review! Addressed all three of the above (as well as the inline comments):

  1. Added multi-threaded fuzz coverage comparing inline and spawned output in order, while preserving the existing randomized configurations and current-thread tests.
  2. Reran the fragmented reproducer on the current head: peak tasks dropped from ~187k to 9, and the slowest SELECT 1 probe from 2.83–2.89 s to 155–165 ms. The description includes these results. Fresh sort_tpch and sort_tpch10 bot runs also exhibit "no change".
  3. Clarified the rustdoc: partitions, clones and concurrent executions share the budget. Its limit is initialized once; reset_state creates a fresh one.

@comphead

Copy link
Copy Markdown
Contributor

run benchmark tpch tpcds

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c6319719743-3728-lr6fs 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing codex/bound-sort-preparation (416c2d6) to fbf7737 (merge-base) diff

Run configuration
run benchmark tpch

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c6319719743-3729-vffg5 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing codex/bound-sort-preparation (416c2d6) to fbf7737 (merge-base) diff

Run configuration
run benchmark tpcds

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing codex/bound-sort-preparation (416c2d6) to fbf7737 (merge-base) diff

Run configuration
run benchmark tpch
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and codex_bound-sort-preparation
--------------------
Benchmark tpch_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query     ┃     HEAD ┃ codex_bound-sort-preparation ┃    Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ QQuery 1  │ 40.74 ms │                     40.35 ms │ no change │
│ QQuery 2  │ 17.95 ms │                     17.91 ms │ no change │
│ QQuery 3  │ 28.32 ms │                     28.38 ms │ no change │
│ QQuery 4  │ 17.31 ms │                     17.07 ms │ no change │
│ QQuery 5  │ 34.97 ms │                     34.82 ms │ no change │
│ QQuery 6  │ 13.61 ms │                     13.43 ms │ no change │
│ QQuery 7  │ 40.98 ms │                     41.49 ms │ no change │
│ QQuery 8  │ 40.96 ms │                     40.76 ms │ no change │
│ QQuery 9  │ 50.01 ms │                     50.17 ms │ no change │
│ QQuery 10 │ 40.67 ms │                     40.24 ms │ no change │
│ QQuery 11 │ 13.34 ms │                     13.27 ms │ no change │
│ QQuery 12 │ 18.33 ms │                     18.15 ms │ no change │
│ QQuery 13 │ 38.95 ms │                     38.95 ms │ no change │
│ QQuery 14 │ 24.26 ms │                     24.45 ms │ no change │
│ QQuery 15 │ 30.74 ms │                     30.41 ms │ no change │
│ QQuery 16 │ 13.36 ms │                     13.55 ms │ no change │
│ QQuery 17 │ 73.19 ms │                     74.41 ms │ no change │
│ QQuery 18 │ 45.04 ms │                     45.07 ms │ no change │
│ QQuery 19 │ 31.76 ms │                     31.98 ms │ no change │
│ QQuery 20 │ 32.12 ms │                     31.77 ms │ no change │
│ QQuery 21 │ 54.21 ms │                     54.37 ms │ no change │
│ QQuery 22 │ 13.56 ms │                     13.46 ms │ no change │
└───────────┴──────────┴──────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Benchmark Summary                           ┃          ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ Total Time (HEAD)                           │ 714.37ms │
│ Total Time (codex_bound-sort-preparation)   │ 714.48ms │
│ Average Time (HEAD)                         │  32.47ms │
│ Average Time (codex_bound-sort-preparation) │  32.48ms │
│ Queries Faster                              │        0 │
│ Queries Slower                              │        0 │
│ Queries with No Change                      │       22 │
│ Queries with Failure                        │        0 │
└─────────────────────────────────────────────┴──────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and codex_bound-sort-preparation
--------------------
Benchmark tpch_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query     ┃                           HEAD ┃   codex_bound-sort-preparation ┃    Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ QQuery 1  │ 40.74 / 42.42 ±0.92 / 43.23 ms │ 40.35 / 41.14 ±1.01 / 43.03 ms │ no change │
│ QQuery 2  │ 17.95 / 18.50 ±0.55 / 19.28 ms │ 17.91 / 18.26 ±0.25 / 18.63 ms │ no change │
│ QQuery 3  │ 28.32 / 28.49 ±0.13 / 28.62 ms │ 28.38 / 28.60 ±0.31 / 29.21 ms │ no change │
│ QQuery 4  │ 17.31 / 17.48 ±0.17 / 17.72 ms │ 17.07 / 17.36 ±0.29 / 17.86 ms │ no change │
│ QQuery 5  │ 34.97 / 35.14 ±0.12 / 35.30 ms │ 34.82 / 35.02 ±0.14 / 35.15 ms │ no change │
│ QQuery 6  │ 13.61 / 13.95 ±0.45 / 14.83 ms │ 13.43 / 13.61 ±0.13 / 13.81 ms │ no change │
│ QQuery 7  │ 40.98 / 42.12 ±0.91 / 43.33 ms │ 41.49 / 42.44 ±0.75 / 43.53 ms │ no change │
│ QQuery 8  │ 40.96 / 42.44 ±2.28 / 46.97 ms │ 40.76 / 41.71 ±1.29 / 44.23 ms │ no change │
│ QQuery 9  │ 50.01 / 51.82 ±1.31 / 53.30 ms │ 50.17 / 51.66 ±1.38 / 54.10 ms │ no change │
│ QQuery 10 │ 40.67 / 41.11 ±0.25 / 41.39 ms │ 40.24 / 40.83 ±0.51 / 41.64 ms │ no change │
│ QQuery 11 │ 13.34 / 13.85 ±0.59 / 14.82 ms │ 13.27 / 13.48 ±0.21 / 13.85 ms │ no change │
│ QQuery 12 │ 18.33 / 18.73 ±0.24 / 19.01 ms │ 18.15 / 18.44 ±0.24 / 18.71 ms │ no change │
│ QQuery 13 │ 38.95 / 39.44 ±0.48 / 40.30 ms │ 38.95 / 40.12 ±1.29 / 42.12 ms │ no change │
│ QQuery 14 │ 24.26 / 24.41 ±0.12 / 24.58 ms │ 24.45 / 24.59 ±0.08 / 24.67 ms │ no change │
│ QQuery 15 │ 30.74 / 31.11 ±0.50 / 32.08 ms │ 30.41 / 31.11 ±0.63 / 32.20 ms │ no change │
│ QQuery 16 │ 13.36 / 13.73 ±0.23 / 14.04 ms │ 13.55 / 13.75 ±0.16 / 13.96 ms │ no change │
│ QQuery 17 │ 73.19 / 74.57 ±1.09 / 76.48 ms │ 74.41 / 74.93 ±0.54 / 75.95 ms │ no change │
│ QQuery 18 │ 45.04 / 45.88 ±0.87 / 47.45 ms │ 45.07 / 45.65 ±0.41 / 46.19 ms │ no change │
│ QQuery 19 │ 31.76 / 33.00 ±1.16 / 35.17 ms │ 31.98 / 32.86 ±1.19 / 35.14 ms │ no change │
│ QQuery 20 │ 32.12 / 32.38 ±0.32 / 32.96 ms │ 31.77 / 31.80 ±0.04 / 31.87 ms │ no change │
│ QQuery 21 │ 54.21 / 55.95 ±1.31 / 57.68 ms │ 54.37 / 56.01 ±1.59 / 58.66 ms │ no change │
│ QQuery 22 │ 13.56 / 13.74 ±0.20 / 14.10 ms │ 13.46 / 13.76 ±0.21 / 14.05 ms │ no change │
└───────────┴────────────────────────────────┴────────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Benchmark Summary                           ┃          ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ Total Time (HEAD)                           │ 730.27ms │
│ Total Time (codex_bound-sort-preparation)   │ 727.12ms │
│ Average Time (HEAD)                         │  33.19ms │
│ Average Time (codex_bound-sort-preparation) │  33.05ms │
│ Queries Faster                              │        0 │
│ Queries Slower                              │        0 │
│ Queries with No Change                      │       22 │
│ Queries with Failure                        │        0 │
└─────────────────────────────────────────────┴──────────┘

Resource Usage

tpch — base (merge-base)

Metric Value
Wall time 5.0s
Peak memory 1.1 GiB
Avg memory 473.6 MiB
CPU user 19.9s
CPU sys 1.8s
Peak spill 0 B

tpch — branch

Metric Value
Wall time 5.0s
Peak memory 1.2 GiB
Avg memory 488.3 MiB
CPU user 19.8s
CPU sys 1.7s
Peak spill 0 B

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing codex/bound-sort-preparation (416c2d6) to fbf7737 (merge-base) diff

Run configuration
run benchmark tpcds
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and codex_bound-sort-preparation
--------------------
Benchmark tpcds_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃       HEAD ┃ codex_bound-sort-preparation ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 1  │    5.75 ms │                      5.91 ms │     no change │
│ QQuery 2  │   81.17 ms │                     82.59 ms │     no change │
│ QQuery 3  │   29.18 ms │                     29.04 ms │     no change │
│ QQuery 4  │  485.36 ms │                    462.30 ms │     no change │
│ QQuery 5  │   51.23 ms │                     51.91 ms │     no change │
│ QQuery 6  │   36.83 ms │                     35.95 ms │     no change │
│ QQuery 7  │   74.44 ms │                     73.49 ms │     no change │
│ QQuery 8  │   37.36 ms │                     36.34 ms │     no change │
│ QQuery 9  │   54.62 ms │                     53.86 ms │     no change │
│ QQuery 10 │   61.01 ms │                     61.88 ms │     no change │
│ QQuery 11 │  270.78 ms │                    292.50 ms │  1.08x slower │
│ QQuery 12 │   29.12 ms │                     29.01 ms │     no change │
│ QQuery 13 │  115.37 ms │                    114.33 ms │     no change │
│ QQuery 14 │  393.12 ms │                    386.16 ms │     no change │
│ QQuery 15 │   58.82 ms │                     53.95 ms │ +1.09x faster │
│ QQuery 16 │    6.46 ms │                      5.92 ms │ +1.09x faster │
│ QQuery 17 │   79.04 ms │                     80.65 ms │     no change │
│ QQuery 18 │  108.84 ms │                    106.81 ms │     no change │
│ QQuery 19 │   42.48 ms │                     42.57 ms │     no change │
│ QQuery 20 │   36.20 ms │                     37.42 ms │     no change │
│ QQuery 21 │   17.37 ms │                     17.73 ms │     no change │
│ QQuery 22 │   68.60 ms │                     65.58 ms │     no change │
│ QQuery 23 │  324.16 ms │                    315.28 ms │     no change │
│ QQuery 24 │  204.67 ms │                    208.31 ms │     no change │
│ QQuery 25 │  108.37 ms │                    111.21 ms │     no change │
│ QQuery 26 │   46.96 ms │                     47.21 ms │     no change │
│ QQuery 27 │    6.28 ms │                      6.18 ms │     no change │
│ QQuery 28 │   56.07 ms │                     57.55 ms │     no change │
│ QQuery 29 │   95.75 ms │                     96.30 ms │     no change │
│ QQuery 30 │   32.89 ms │                     32.79 ms │     no change │
│ QQuery 31 │  109.86 ms │                    108.75 ms │     no change │
│ QQuery 32 │   20.36 ms │                     19.88 ms │     no change │
│ QQuery 33 │   36.92 ms │                     36.38 ms │     no change │
│ QQuery 34 │   10.42 ms │                      9.96 ms │     no change │
│ QQuery 35 │   73.00 ms │                     73.55 ms │     no change │
│ QQuery 36 │    6.26 ms │                      6.37 ms │     no change │
│ QQuery 37 │    7.14 ms │                      7.39 ms │     no change │
│ QQuery 38 │   65.67 ms │                     64.25 ms │     no change │
│ QQuery 39 │   76.05 ms │                     73.76 ms │     no change │
│ QQuery 40 │   24.30 ms │                     24.35 ms │     no change │
│ QQuery 41 │   12.77 ms │                     12.84 ms │     no change │
│ QQuery 42 │   24.68 ms │                     25.02 ms │     no change │
│ QQuery 43 │    5.37 ms │                      5.68 ms │  1.06x slower │
│ QQuery 44 │    9.38 ms │                      9.69 ms │     no change │
│ QQuery 45 │   42.44 ms │                     42.51 ms │     no change │
│ QQuery 46 │   13.07 ms │                     13.07 ms │     no change │
│ QQuery 47 │  241.54 ms │                    239.33 ms │     no change │
│ QQuery 48 │   92.42 ms │                     91.64 ms │     no change │
│ QQuery 49 │   69.63 ms │                     69.90 ms │     no change │
│ QQuery 50 │   58.28 ms │                     58.30 ms │     no change │
│ QQuery 51 │   92.83 ms │                     95.81 ms │     no change │
│ QQuery 52 │   24.35 ms │                     24.48 ms │     no change │
│ QQuery 53 │   29.42 ms │                     30.07 ms │     no change │
│ QQuery 54 │   53.93 ms │                     55.41 ms │     no change │
│ QQuery 55 │   24.70 ms │                     24.59 ms │     no change │
│ QQuery 56 │   39.05 ms │                     39.94 ms │     no change │
│ QQuery 57 │  169.59 ms │                    172.00 ms │     no change │
│ QQuery 58 │  106.91 ms │                    109.21 ms │     no change │
│ QQuery 59 │  116.38 ms │                    117.69 ms │     no change │
│ QQuery 60 │   39.07 ms │                     39.13 ms │     no change │
│ QQuery 61 │   10.73 ms │                     10.96 ms │     no change │
│ QQuery 62 │   44.30 ms │                     44.74 ms │     no change │
│ QQuery 63 │   29.23 ms │                     29.59 ms │     no change │
│ QQuery 64 │  370.20 ms │                    371.84 ms │     no change │
│ QQuery 65 │  135.86 ms │                    137.73 ms │     no change │
│ QQuery 66 │   76.64 ms │                     77.27 ms │     no change │
│ QQuery 67 │  265.55 ms │                    262.48 ms │     no change │
│ QQuery 68 │   12.51 ms │                     12.27 ms │     no change │
│ QQuery 69 │   57.79 ms │                     56.94 ms │     no change │
│ QQuery 70 │  105.18 ms │                    104.24 ms │     no change │
│ QQuery 71 │   35.85 ms │                     36.25 ms │     no change │
│ QQuery 72 │ 1741.53 ms │                   1759.16 ms │     no change │
│ QQuery 73 │   10.37 ms │                     10.83 ms │     no change │
│ QQuery 74 │  166.51 ms │                    168.84 ms │     no change │
│ QQuery 75 │  143.59 ms │                    141.20 ms │     no change │
│ QQuery 76 │   35.34 ms │                     34.36 ms │     no change │
│ QQuery 77 │   62.17 ms │                     59.60 ms │     no change │
│ QQuery 78 │  165.40 ms │                    163.31 ms │     no change │
│ QQuery 79 │   69.12 ms │                     68.15 ms │     no change │
│ QQuery 80 │   98.39 ms │                     96.70 ms │     no change │
│ QQuery 81 │   26.43 ms │                     25.85 ms │     no change │
│ QQuery 82 │   17.15 ms │                     17.00 ms │     no change │
│ QQuery 83 │   33.83 ms │                     33.86 ms │     no change │
│ QQuery 84 │   30.99 ms │                     29.97 ms │     no change │
│ QQuery 85 │  102.07 ms │                     99.92 ms │     no change │
│ QQuery 86 │   26.45 ms │                     26.69 ms │     no change │
│ QQuery 87 │   64.38 ms │                     65.95 ms │     no change │
│ QQuery 88 │   60.84 ms │                     58.91 ms │     no change │
│ QQuery 89 │   36.39 ms │                     36.04 ms │     no change │
│ QQuery 90 │   17.04 ms │                     16.96 ms │     no change │
│ QQuery 91 │   42.94 ms │                     42.95 ms │     no change │
│ QQuery 92 │   30.60 ms │                     30.65 ms │     no change │
│ QQuery 93 │   50.15 ms │                     51.29 ms │     no change │
│ QQuery 94 │   39.34 ms │                     38.73 ms │     no change │
│ QQuery 95 │   82.64 ms │                     82.95 ms │     no change │
│ QQuery 96 │   23.86 ms │                     24.09 ms │     no change │
│ QQuery 97 │   51.27 ms │                     51.87 ms │     no change │
│ QQuery 98 │   45.75 ms │                     43.65 ms │     no change │
│ QQuery 99 │   66.71 ms │                     65.69 ms │     no change │
└───────────┴────────────┴──────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Benchmark Summary                           ┃           ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Total Time (HEAD)                           │ 9199.16ms │
│ Total Time (codex_bound-sort-preparation)   │ 9191.19ms │
│ Average Time (HEAD)                         │   92.92ms │
│ Average Time (codex_bound-sort-preparation) │   92.84ms │
│ Queries Faster                              │         2 │
│ Queries Slower                              │         2 │
│ Queries with No Change                      │        95 │
│ Queries with Failure                        │         0 │
└─────────────────────────────────────────────┴───────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and codex_bound-sort-preparation
--------------------
Benchmark tpcds_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃                                  HEAD ┃          codex_bound-sort-preparation ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 1  │           5.75 / 6.29 ±0.93 / 8.15 ms │           5.91 / 6.41 ±0.92 / 8.24 ms │     no change │
│ QQuery 2  │        81.17 / 81.77 ±0.80 / 83.35 ms │        82.59 / 83.27 ±0.67 / 84.53 ms │     no change │
│ QQuery 3  │        29.18 / 29.55 ±0.23 / 29.83 ms │        29.04 / 29.29 ±0.15 / 29.49 ms │     no change │
│ QQuery 4  │     485.36 / 497.93 ±8.24 / 509.10 ms │    462.30 / 479.99 ±14.14 / 500.13 ms │     no change │
│ QQuery 5  │        51.23 / 51.66 ±0.46 / 52.24 ms │        51.91 / 55.18 ±5.00 / 65.13 ms │  1.07x slower │
│ QQuery 6  │        36.83 / 37.25 ±0.30 / 37.64 ms │        35.95 / 36.52 ±0.47 / 37.17 ms │     no change │
│ QQuery 7  │        74.44 / 74.66 ±0.18 / 74.87 ms │        73.49 / 74.25 ±0.39 / 74.53 ms │     no change │
│ QQuery 8  │        37.36 / 38.99 ±2.64 / 44.19 ms │        36.34 / 36.70 ±0.36 / 37.35 ms │ +1.06x faster │
│ QQuery 9  │        54.62 / 56.00 ±1.99 / 59.93 ms │        53.86 / 56.42 ±3.06 / 62.36 ms │     no change │
│ QQuery 10 │        61.01 / 61.91 ±0.59 / 62.61 ms │        61.88 / 62.29 ±0.25 / 62.68 ms │     no change │
│ QQuery 11 │    270.78 / 290.30 ±15.05 / 308.25 ms │     292.50 / 306.60 ±9.60 / 318.78 ms │  1.06x slower │
│ QQuery 12 │        29.12 / 29.56 ±0.22 / 29.73 ms │        29.01 / 29.24 ±0.29 / 29.82 ms │     no change │
│ QQuery 13 │     115.37 / 116.58 ±1.19 / 118.71 ms │     114.33 / 115.73 ±1.27 / 117.99 ms │     no change │
│ QQuery 14 │     393.12 / 397.34 ±2.36 / 400.29 ms │     386.16 / 392.76 ±3.95 / 396.29 ms │     no change │
│ QQuery 15 │        58.82 / 61.25 ±2.76 / 66.32 ms │        53.95 / 54.80 ±0.84 / 56.37 ms │ +1.12x faster │
│ QQuery 16 │           6.46 / 6.60 ±0.10 / 6.72 ms │           5.92 / 6.21 ±0.29 / 6.66 ms │ +1.06x faster │
│ QQuery 17 │        79.04 / 80.93 ±1.10 / 82.43 ms │        80.65 / 81.39 ±0.81 / 82.92 ms │     no change │
│ QQuery 18 │     108.84 / 111.98 ±2.90 / 116.82 ms │     106.81 / 108.66 ±1.98 / 111.77 ms │     no change │
│ QQuery 19 │        42.48 / 42.93 ±0.36 / 43.53 ms │        42.57 / 43.06 ±0.40 / 43.58 ms │     no change │
│ QQuery 20 │        36.20 / 36.83 ±0.40 / 37.35 ms │        37.42 / 37.94 ±0.56 / 38.91 ms │     no change │
│ QQuery 21 │        17.37 / 17.67 ±0.22 / 17.93 ms │        17.73 / 18.05 ±0.28 / 18.53 ms │     no change │
│ QQuery 22 │        68.60 / 71.49 ±2.07 / 74.37 ms │        65.58 / 67.82 ±1.77 / 71.00 ms │ +1.05x faster │
│ QQuery 23 │     324.16 / 330.30 ±3.84 / 334.71 ms │    315.28 / 329.72 ±13.94 / 355.32 ms │     no change │
│ QQuery 24 │     204.67 / 209.73 ±2.96 / 213.75 ms │     208.31 / 210.53 ±1.90 / 213.97 ms │     no change │
│ QQuery 25 │     108.37 / 114.87 ±6.77 / 127.51 ms │     111.21 / 114.77 ±3.76 / 122.08 ms │     no change │
│ QQuery 26 │        46.96 / 47.14 ±0.19 / 47.49 ms │        47.21 / 47.76 ±0.42 / 48.22 ms │     no change │
│ QQuery 27 │           6.28 / 6.31 ±0.04 / 6.38 ms │           6.18 / 6.29 ±0.11 / 6.46 ms │     no change │
│ QQuery 28 │        56.07 / 61.62 ±5.03 / 70.61 ms │        57.55 / 58.94 ±1.91 / 62.72 ms │     no change │
│ QQuery 29 │       95.75 / 98.21 ±2.11 / 101.03 ms │      96.30 / 100.61 ±6.63 / 113.63 ms │     no change │
│ QQuery 30 │        32.89 / 33.57 ±0.40 / 33.98 ms │        32.79 / 33.23 ±0.34 / 33.57 ms │     no change │
│ QQuery 31 │     109.86 / 110.95 ±0.83 / 111.84 ms │     108.75 / 109.83 ±1.35 / 112.44 ms │     no change │
│ QQuery 32 │        20.36 / 20.77 ±0.49 / 21.73 ms │        19.88 / 20.08 ±0.17 / 20.38 ms │     no change │
│ QQuery 33 │        36.92 / 37.30 ±0.25 / 37.61 ms │        36.38 / 37.17 ±0.57 / 38.06 ms │     no change │
│ QQuery 34 │        10.42 / 10.74 ±0.16 / 10.88 ms │         9.96 / 10.13 ±0.18 / 10.41 ms │ +1.06x faster │
│ QQuery 35 │        73.00 / 74.28 ±0.84 / 75.27 ms │        73.55 / 76.51 ±1.87 / 78.37 ms │     no change │
│ QQuery 36 │           6.26 / 6.34 ±0.09 / 6.51 ms │           6.37 / 6.47 ±0.10 / 6.61 ms │     no change │
│ QQuery 37 │          7.14 / 8.06 ±1.60 / 11.25 ms │           7.39 / 7.45 ±0.04 / 7.52 ms │ +1.08x faster │
│ QQuery 38 │        65.67 / 66.81 ±1.42 / 69.51 ms │        64.25 / 65.05 ±0.59 / 65.89 ms │     no change │
│ QQuery 39 │        76.05 / 76.99 ±0.78 / 78.03 ms │        73.76 / 74.22 ±0.24 / 74.47 ms │     no change │
│ QQuery 40 │        24.30 / 24.58 ±0.17 / 24.78 ms │        24.35 / 25.09 ±0.44 / 25.56 ms │     no change │
│ QQuery 41 │        12.77 / 12.87 ±0.07 / 12.96 ms │        12.84 / 13.04 ±0.23 / 13.46 ms │     no change │
│ QQuery 42 │        24.68 / 26.65 ±2.65 / 31.89 ms │        25.02 / 25.59 ±0.53 / 26.30 ms │     no change │
│ QQuery 43 │           5.37 / 5.42 ±0.06 / 5.53 ms │          5.68 / 7.74 ±4.04 / 15.82 ms │  1.43x slower │
│ QQuery 44 │           9.38 / 9.52 ±0.13 / 9.77 ms │          9.69 / 9.90 ±0.24 / 10.35 ms │     no change │
│ QQuery 45 │        42.44 / 44.35 ±1.56 / 46.51 ms │        42.51 / 44.87 ±2.24 / 48.90 ms │     no change │
│ QQuery 46 │        13.07 / 13.42 ±0.30 / 13.88 ms │        13.07 / 13.67 ±0.45 / 14.43 ms │     no change │
│ QQuery 47 │     241.54 / 249.69 ±6.00 / 258.84 ms │    239.33 / 251.04 ±10.32 / 269.36 ms │     no change │
│ QQuery 48 │       92.42 / 95.12 ±2.74 / 100.32 ms │        91.64 / 92.93 ±1.39 / 95.61 ms │     no change │
│ QQuery 49 │        69.63 / 70.86 ±1.24 / 72.42 ms │        69.90 / 70.80 ±0.71 / 71.98 ms │     no change │
│ QQuery 50 │        58.28 / 58.52 ±0.19 / 58.85 ms │        58.30 / 58.60 ±0.17 / 58.79 ms │     no change │
│ QQuery 51 │        92.83 / 95.21 ±1.91 / 98.32 ms │       95.81 / 99.94 ±5.14 / 109.44 ms │     no change │
│ QQuery 52 │        24.35 / 25.12 ±1.08 / 27.22 ms │        24.48 / 24.67 ±0.21 / 25.02 ms │     no change │
│ QQuery 53 │        29.42 / 29.76 ±0.50 / 30.74 ms │        30.07 / 30.62 ±0.33 / 31.03 ms │     no change │
│ QQuery 54 │        53.93 / 54.47 ±0.42 / 55.17 ms │        55.41 / 56.02 ±0.44 / 56.70 ms │     no change │
│ QQuery 55 │        24.70 / 24.79 ±0.08 / 24.91 ms │        24.59 / 24.83 ±0.14 / 25.03 ms │     no change │
│ QQuery 56 │        39.05 / 42.15 ±4.65 / 51.41 ms │        39.94 / 40.85 ±0.79 / 42.08 ms │     no change │
│ QQuery 57 │     169.59 / 172.55 ±3.02 / 178.24 ms │     172.00 / 175.83 ±2.80 / 179.34 ms │     no change │
│ QQuery 58 │     106.91 / 110.91 ±2.52 / 114.77 ms │     109.21 / 110.73 ±2.15 / 114.98 ms │     no change │
│ QQuery 59 │     116.38 / 117.82 ±1.03 / 119.33 ms │     117.69 / 119.81 ±2.58 / 124.90 ms │     no change │
│ QQuery 60 │        39.07 / 40.32 ±1.06 / 41.67 ms │        39.13 / 39.89 ±0.75 / 41.13 ms │     no change │
│ QQuery 61 │        10.73 / 10.91 ±0.17 / 11.16 ms │        10.96 / 11.05 ±0.09 / 11.19 ms │     no change │
│ QQuery 62 │        44.30 / 44.55 ±0.28 / 45.05 ms │        44.74 / 45.00 ±0.34 / 45.65 ms │     no change │
│ QQuery 63 │        29.23 / 29.76 ±0.43 / 30.52 ms │        29.59 / 29.80 ±0.18 / 30.09 ms │     no change │
│ QQuery 64 │     370.20 / 371.81 ±1.47 / 374.33 ms │     371.84 / 381.16 ±5.38 / 385.70 ms │     no change │
│ QQuery 65 │     135.86 / 142.02 ±6.85 / 155.20 ms │     137.73 / 139.83 ±1.37 / 141.51 ms │     no change │
│ QQuery 66 │        76.64 / 78.59 ±1.18 / 80.01 ms │        77.27 / 78.57 ±0.83 / 79.47 ms │     no change │
│ QQuery 67 │     265.55 / 276.36 ±7.96 / 286.09 ms │     262.48 / 271.42 ±5.27 / 277.12 ms │     no change │
│ QQuery 68 │        12.51 / 12.60 ±0.10 / 12.78 ms │        12.27 / 12.47 ±0.16 / 12.65 ms │     no change │
│ QQuery 69 │        57.79 / 59.10 ±0.81 / 60.11 ms │        56.94 / 57.95 ±0.72 / 59.00 ms │     no change │
│ QQuery 70 │     105.18 / 106.41 ±0.85 / 107.44 ms │     104.24 / 104.64 ±0.35 / 105.13 ms │     no change │
│ QQuery 71 │        35.85 / 39.34 ±6.22 / 51.77 ms │        36.25 / 37.52 ±1.67 / 40.74 ms │     no change │
│ QQuery 72 │ 1741.53 / 1805.16 ±35.35 / 1848.49 ms │ 1759.16 / 1834.14 ±46.22 / 1887.43 ms │     no change │
│ QQuery 73 │        10.37 / 11.03 ±0.63 / 12.10 ms │        10.83 / 11.46 ±0.59 / 12.36 ms │     no change │
│ QQuery 74 │     166.51 / 178.35 ±8.02 / 190.81 ms │     168.84 / 180.27 ±6.43 / 186.52 ms │     no change │
│ QQuery 75 │     143.59 / 145.33 ±2.22 / 149.49 ms │     141.20 / 142.54 ±1.76 / 145.92 ms │     no change │
│ QQuery 76 │        35.34 / 38.48 ±5.68 / 49.83 ms │        34.36 / 35.15 ±0.82 / 36.57 ms │ +1.09x faster │
│ QQuery 77 │        62.17 / 63.96 ±1.56 / 65.81 ms │        59.60 / 60.61 ±0.78 / 61.87 ms │ +1.06x faster │
│ QQuery 78 │     165.40 / 174.18 ±5.19 / 181.19 ms │     163.31 / 166.11 ±2.76 / 171.28 ms │     no change │
│ QQuery 79 │        69.12 / 69.69 ±0.51 / 70.51 ms │        68.15 / 69.54 ±1.12 / 70.98 ms │     no change │
│ QQuery 80 │      98.39 / 101.63 ±3.86 / 109.07 ms │       96.70 / 98.56 ±1.21 / 100.47 ms │     no change │
│ QQuery 81 │        26.43 / 26.84 ±0.28 / 27.20 ms │        25.85 / 26.21 ±0.31 / 26.72 ms │     no change │
│ QQuery 82 │        17.15 / 17.59 ±0.33 / 18.18 ms │        17.00 / 17.21 ±0.17 / 17.43 ms │     no change │
│ QQuery 83 │        33.83 / 34.06 ±0.25 / 34.54 ms │        33.86 / 34.09 ±0.23 / 34.50 ms │     no change │
│ QQuery 84 │        30.99 / 31.37 ±0.25 / 31.78 ms │        29.97 / 30.09 ±0.12 / 30.28 ms │     no change │
│ QQuery 85 │     102.07 / 106.38 ±4.77 / 115.63 ms │      99.92 / 105.09 ±7.25 / 119.22 ms │     no change │
│ QQuery 86 │        26.45 / 27.83 ±1.17 / 29.85 ms │        26.69 / 26.88 ±0.19 / 27.18 ms │     no change │
│ QQuery 87 │        64.38 / 66.50 ±1.70 / 68.97 ms │        65.95 / 67.00 ±1.16 / 69.08 ms │     no change │
│ QQuery 88 │        60.84 / 61.57 ±0.40 / 61.93 ms │        58.91 / 60.18 ±0.75 / 61.17 ms │     no change │
│ QQuery 89 │        36.39 / 38.47 ±3.34 / 45.13 ms │        36.04 / 36.72 ±0.78 / 38.22 ms │     no change │
│ QQuery 90 │        17.04 / 17.29 ±0.23 / 17.69 ms │        16.96 / 17.14 ±0.13 / 17.32 ms │     no change │
│ QQuery 91 │        42.94 / 44.26 ±1.48 / 47.15 ms │        42.95 / 44.01 ±0.90 / 45.25 ms │     no change │
│ QQuery 92 │        30.60 / 31.30 ±0.53 / 32.22 ms │        30.65 / 31.05 ±0.33 / 31.45 ms │     no change │
│ QQuery 93 │        50.15 / 51.01 ±0.83 / 52.53 ms │        51.29 / 52.08 ±0.85 / 53.66 ms │     no change │
│ QQuery 94 │        39.34 / 40.08 ±0.48 / 40.67 ms │        38.73 / 39.07 ±0.27 / 39.50 ms │     no change │
│ QQuery 95 │        82.64 / 84.23 ±2.05 / 88.28 ms │        82.95 / 85.45 ±1.68 / 88.02 ms │     no change │
│ QQuery 96 │        23.86 / 23.99 ±0.12 / 24.15 ms │        24.09 / 24.36 ±0.19 / 24.67 ms │     no change │
│ QQuery 97 │        51.27 / 52.26 ±1.39 / 54.99 ms │        51.87 / 52.67 ±0.64 / 53.84 ms │     no change │
│ QQuery 98 │        45.75 / 47.05 ±0.90 / 48.20 ms │        43.65 / 46.12 ±2.58 / 51.02 ms │     no change │
│ QQuery 99 │        66.71 / 69.31 ±3.31 / 75.83 ms │        65.69 / 66.53 ±0.74 / 67.49 ms │     no change │
└───────────┴───────────────────────────────────────┴───────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Benchmark Summary                           ┃           ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Total Time (HEAD)                           │ 9468.18ms │
│ Total Time (codex_bound-sort-preparation)   │ 9457.52ms │
│ Average Time (HEAD)                         │   95.64ms │
│ Average Time (codex_bound-sort-preparation) │   95.53ms │
│ Queries Faster                              │         8 │
│ Queries Slower                              │         3 │
│ Queries with No Change                      │        88 │
│ Queries with Failure                        │         0 │
└─────────────────────────────────────────────┴───────────┘

Resource Usage

tpcds — base (merge-base)

Metric Value
Wall time 50.0s
Peak memory 2.0 GiB
Avg memory 1.4 GiB
CPU user 206.8s
CPU sys 6.1s
Peak spill 0 B

tpcds — branch

Metric Value
Wall time 50.0s
Peak memory 2.0 GiB
Avg memory 1.4 GiB
CPU user 207.4s
CPU sys 6.0s
Peak spill 0 B

File an issue against this benchmark runner

@comphead comphead left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for addressing the earlier points. A few design questions after a second pass.

.into_iter()
.map(|stream| spawn_buffered(stream, 1))
.collect();
let concurrency = SortConcurrency::for_current_runtime(&self.sort_concurrency);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The bound caps the task count, but I think the root cause is the run count. 187k runs is SF1 lineitem at one run per 32-row batch. With this PR the merge still has 187k streams, each with its own lookahead batch (the gap tracked in #26411).

The run count stays at one per input batch because coalesce_in_mem_batches_into_runs only runs on the output path and only for single-column sorts (coalesce_runs && self.expr.len() == 1 just above). Multi-column sorts and every spill keep one run per input batch.

Would a minimum run size, for example coalescing runs below batch_size rows regardless of column count, fix the reported case more directly? It would cut task count, merge fan-in and lookahead memory together. For comparison, the description reports at most 123 ms for 8192-row batches on main and 155 to 165 ms for this PR on 32-row batches. Those are separate runs, so this is only indicative. The semaphore would still make sense as a backstop for very large inputs. This could also be a follow-up if you prefer to keep this PR focused.

@geoffreyclaude geoffreyclaude Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I tested this on my fork, in [#22](geoffreyclaude#22). Coalescing made fragmented-input sorts 19 to 79% faster (depending on the benched scenario), with essentially unchanged performance on the ordinary preloaded wide sort. This definitely looks worth pursuing separately: I can open a follow-up PR as the gains are massive on small batches, and the code change is pretty simple.

To be clear though, our production case involved very large inputs with 8,192-row batches. The 32-row batches simply stress run count with a smaller dataset and they aren’t intended to represent typical workloads. Coalescing helps fragmented inputs, while the task bound remains necessary for the production case.

// Ordered buffering would keep completed runs behind a slow earlier run,
// filling the window and preventing new jobs from starting.
let jobs = futures::stream::iter(jobs)
.try_buffer_unordered(concurrency.per_partition_limit);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One job per run is what makes the per-partition window, the unordered buffering, the (index, stream) reassembly and collect_prepared_runs necessary. An alternative is to split the runs into at most per_partition_limit contiguous, byte-balanced chunks, with one job sorting each chunk sequentially.

  • All jobs are in flight at once, so ordered try_buffered has no head-of-line blocking and run order is preserved without index bookkeeping.
  • Per-job overhead (permit, spawn, join, boxed stream) is paid per chunk rather than per run. With 32-row runs that overhead is likely comparable to the sort itself. I have not measured this.
  • The shared semaphore still bounds jobs across partitions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I also tested this on my fork, in #21. It reduced unrelated SELECT 1 probe delays (because fewer workers were busy initializing merges simultaneously), but made wide and uneven-work sorts 6 to 13% slower (because, even with byte-balanced chunks, some workers still finish early while others have substantial sorting left).

It does simplify result collection, but adds group balancing and more involved memory-reservation ownership.

Given those tradeoffs, I’d prefer keeping the current per-run design here.

Self::collect_prepared_runs(run_count, jobs).await
}

/// Restore input order because the merge uses it to break equal-key ties.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Small wording point. The merge does not preserve run order for equal keys, for two reasons.

  • Each run is sorted with lexsort_to_indices, which arrow documents as an unstable sort.
  • StreamingMergeBuilder defaults to enable_round_robin_tie_breaker: true and in_mem_sort_stream does not override it, so equal keys are interleaved across runs by poll count.

What the reassembly does guarantee is deterministic output regardless of job completion order, which is still worth keeping. I would phrase this comment, the one on prepare_sort_runs and the description that way. This also corrects my earlier comment on spill_tests.rs, which framed it as tie stability. The new tests are fine as determinism checks.

@geoffreyclaude geoffreyclaude Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed: I've updated the relevant comments in docs: clarify sort run ordering guarantee.

@geoffreyclaude

geoffreyclaude commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for addressing the earlier points. A few design questions after a second pass.

@comphead Thanks for your very pertinent suggestions! I tested both on my fork (thank you Codex!), then investigated where the scheduling gains came from:

  • #22: coalesce small batches. Fragmented-input sorts improved by 19 to 79%. This looks worth pursuing separately. Large inputs with normal-sized batches still need the task bound here.
  • #21: fixed groups of runs. Unrelated query delays improved substantially, but wide and uneven-work sorts became 6 to 13% slower. It simplifies result collection while adding group balancing and more involved reservation ownership. I’d prefer keeping the current per-run design.
  • #23: yield during merge row-key initialization. Traces pointed to merge initialization occupying all workers. Yielding directly there preserved the responsiveness gains without grouping runs: on wide input with 8,192-row batches, the median worst probe delay per run fell from 49.6 to 4.5 ms, with sort time changing by +0.7% (within noise).

My proposed path is to keep bounded per-run preparation in this PR, then pursue coalescing and cooperative merge initialization as separate improvements in two new follow-up PRs.

I also corrected the ordering comments and description: restoring run order keeps output independent of task completion order.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Core DataFusion crate physical-plan Changes to the physical-plan crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants