Allocation discipline on the per-row paths¶
Applies to: windowed/session aggregation, GROUP BY, Top-N,
the updating join, OVER, deduplication
Beyond the arrow-row and mini-batch mechanisms covered elsewhere, a series of targeted fixes removed allocations and redundant per-row work from specific hot loops: reuse instead of realloc, move instead of clone, batch instead of loop, and — where the access pattern actually fits — a columnar kernel instead of a row loop at all.
Per-row allocation cuts¶
- Reuse the per-row window buffer instead of allocating one per row (26% on tumbling,
3833e8d). - Move the grouping key into its last window instead of cloning it (~18% keyed,
ffec81e). - Reach existing groups by
get_mutand clone the key only on insert (~8% on string keys,6802752). - Defer owning a Top-N row until it is known to enter the buffer, and share the payload via
Arcso the with-rank cascade's double emits are refcount bumps instead of row deep-clones (q19 0.76x → 1.13x, q18 0.82x → 1.28x,22f5c0f). - Move the key/row into join state instead of re-cloning it on insert (
c597142).
Batch the per-row folds¶
- The running
OVERaggregate replaced a DataFusion update-batch-then-evaluate call per row with a small typed running state folded directly (~2.6x,945d3da). - The INNER updating join gathers all of a batch's candidate pairs, evaluates the residual predicate
columnar in one pass, and emits by
filter_record_batch— one convert/eval per batch instead of per row (q9 0.39x → ~1.0x,4429e2f); associated rows in the residual path bulk-decode in oneconvert_rowscall (q7 0.33x → 0.74x,ed74dac). - The session aggregator segments each key's rows into gap-connected runs so a run pays one value
slice and one accumulator update, with the merge scan a bounded O(log n) range probe (9.4x on
dense sessions,
62dffda).
Columnar-kernel internal state where it fits¶
Keep-first dedup holds its per-key candidates as a single Arrow batch — one row per pending key —
reduced per input batch with filter/take/concat kernels, reading only the key and the rowtime per
row, the same minimal per-row read Arrow's own hash aggregate does; it never boxes rows into scalars
(ebfde70).
This was deliberately not applied to window Top-N: bounded ranking with arrival-order tie-breaking maps poorly onto columnar kernels, so its buffer stays row-oriented, as it does in Arroyo and RisingWave.