avantgardnerio opened a new pull request, #2216:
URL: https://github.com/apache/datafusion-ballista/pull/2216

   ## Summary
   
   Sketch-side prep for the eventual TDigest → KLL swap in `RuntimeStatsExec` — 
the TODO on `ballista/core/src/execution_plans/runtime_stats.rs:39`. This PR 
isn't the swap itself; it argues (with numbers) that the swap is affordable in 
both the URRE and ORRE arms.
   
   - `absorb(&[T])` (iterator) + `absorb_slice(&[T])` — batch-oriented ingest, 
amortizes `compact_all`.
   - Per-level sortedness invariant — `compact_level` skips re-sort when the 
level was left sorted by an earlier promotion; promotions merge-extend in 
linear time when the destination is sorted.
   - `absorb_sorted_slice(&[T])` — for callers that can guarantee sorted input, 
keeps level 0 permanently sorted and skips *every* compaction sort.
   - Statistical test coverage against the DataSketches empirical bound 
(uniform, clustered, heavy-ties).
   
   The actual `RuntimeStatsExec` integration, wire format for KLL sketches 
across the scheduler boundary, and the ORRE plan-shape follow-up are all out of 
scope here — separate PRs.
   
   ## The two arms
   
   **ORRE arm (ordered input, requires a follow-up plan-shape move):**
   
   Current:
   ```
   Scan ──▶ RuntimeStats ──▶ Sort ──▶ ORRE ──▶ ShuffleWrite
              sketches unsorted rows; Sort then does the same work again
   ```
   
   Follow-up:
   ```
   Scan ──▶ Sort ──▶ RuntimeStats ──▶ DamExec ──▶ ORRE ──▶ ShuffleWrite
                       KLL<OrderedFloat<f64>>::absorb_sorted_slice
                       7.3 ms / 1M rows       0.53× TDigest (1.9× faster)
   ```
   
   `SortExec` is already required by ORRE. Moving it above `RuntimeStats` costs 
nothing plan-wise — the same rows get sorted the same way. `DamExec` (from 
Ballista's existing operator vocabulary) holds sorted rows until the 
scheduler's global cuts arrive. The sketch side is landed here; the planner 
move is a separate PR.
   
   **URRE arm (unordered input, ships as-is with this PR's shape):**
   
   ```
   Scan ──▶ RuntimeStats ──▶ URRE ──▶ ShuffleWrite
              KLL<OrderedFloat<f64>>::absorb_slice
              25.1 ms / 1M rows       1.8× TDigest
   ```
   
   ## Why swap TDigest for KLL at all
   
   TDigest at `TDIGEST_MAX_SIZE=100` can only sketch a single `f64` column. KLL 
is generic over `T: Ord + Clone`, and composes with `arrow::row::OwnedRow` to 
cover the full ORDER BY grammar for partition-boundary picking: multi-column 
keys, nullable, per-column ASC/DESC, configurable NULLS FIRST/LAST. That 
grammar is what parallel window (and later merge-join, order-by execution) 
actually needs from the boundaries.
   
   ## Numbers (uniform f64, 1M rows, `k=800` for KLL, `TDIGEST_MAX_SIZE=100`)
   
   | variant | time | throughput | vs TDigest |
   |---|---:|---:|---:|
   | TDigest (baseline) | 13.8 ms | 72 M/s | 1.0× |
   | KLL<OrderedFloat<f64>> `insert` (per-row) | 48.3 ms | 21 M/s | 3.5× |
   | KLL<OrderedFloat<f64>> `absorb_slice` (this PR, URRE arm) | 25.1 ms | 40 
M/s | 1.8× |
   | KLL<OrderedFloat<f64>> `absorb_sorted_slice` (this PR + planner follow-up, 
ORRE arm) | 7.3 ms | 138 M/s | **0.53× (1.9× faster)** |
   | KLL<OwnedRow> `absorb` (multi-column path, this PR) | 80.1 ms | 12 M/s | 
5.7× |
   
   The `OwnedRow` path stays a real regression (~5.7×) — the compaction cost 
for heap-allocated rows is dominated by cache misses on the `OwnedRow` storage, 
not sort-algorithm efficiency. Closing that gap is deferred-optimization item 
(2) in the module doc (batch-oriented level-0-borrowed-row ingest), noted as a 
TODO on the interim iterator API.
   
   ## Test plan
   
   - [x] `cargo test -p ballista-core kll::` — 20/20 pass in 0.7s
   - [x] `cargo bench --bench quantile_sketch -p ballista-benchmarks` — numbers 
reproducible
   - [x] `cargo clippy --all-targets` clean, `cargo fmt --all` applied
   
   Coverage:
   - Bit-identical between `insert` / `absorb` / `absorb_slice` on unique-value 
streams.
   - Bit-identical between `absorb_slice` / `absorb_sorted_slice` on sorted 
unique streams.
   - DataSketches P99 rank-error bound (`2.296 / k^0.9723`, source: 
`datasketches-cpp/kll/include/kll_sketch_impl.hpp:619`) holds at ≤5% fail rate 
across 100 seeds on uniform, clustered, and heavy-ties distributions.
   - 4-way split + merge produces same quantiles as single sketch within 2ε.
   - `debug_assert!` catches unsorted input to `absorb_sorted_slice`.
   
   Not tested here (belongs on the swap PR):
   - Actual `RuntimeStatsExec` integration.
   - Wire format for KLL sketches across the scheduler boundary.
   - End-to-end TPC-H / parallel-window benchmarks.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to