compute: let hinted TopK stages emit the rows they keep - #38672
compute: let hinted TopK stages emit the rows they keep#38672frankmcsherry wants to merge 5 commits into
Conversation
A basic TopK stage emits negations of the rows it drops and its result is the input plus those negations, so its state is proportional to the dropped rows and a final consolidate cancels them. When the expected group size hint says a key holds at least twice what a stage keeps, emit the kept rows instead: the reduce output is the stage's result, already consolidated, and the state is the kept rows. Behind enable_compute_topk_retained_stages, default off, on in CI. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The initial map hashed each row and packed (hash, group), and the first stage mapped that to (hash % modulus, group) and repacked. Compute the first stage's bucket in the initial map, zero when the only stage is the final one, and let a stage rekey only when its input is keyed for an earlier stage, as the min/max hierarchy already does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… prefix Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Split a row's bytes at a datum boundary without decoding past it. Also part of MaterializeInc#38665; whichever lands first carries it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
BasicTopKPlan is part of the durably stored LIR schema, so a new field means a version bump. The bucket list already says what the rule needs: it is empty exactly when the hint was at most sixteen, so the lone final stage keeps rows when twice its kept count fits in sixteen, and bucket stages keep emitting dropped rows. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
QA LLM Review1. HIGH -- Basic TopK with
|
A basic TopK stage emits negations of the rows it drops, and the stage's result is its input plus those negations. That keeps the reduce's state proportional to the dropped rows, which is small when groups are barely larger than the limit and large otherwise. For
DISTINCT ONover groups of eight rows with anEXPECTED GROUP SIZEhint, the final stage's arrangement held 8.75M records for a 1.25M-row result, and the consolidate that cancels the negations processed 18.75M records and was the largest operator in the dataflow. Every hierarchical stage after the first is in the same position: it sees at most sixteen times the limit per key and drops most of it.This change lets a stage emit the rows it keeps instead, chosen per stage:
BasicTopKPlanis part of the durably stored LIR schema and is unchanged.The behaviour is behind
enable_compute_topk_retained_stages, default off in production and on in the CI configuration, and registered with the parallel workload's flag flipper.Hydration of
SELECT DISTINCT ON (g) g, v FROM ... OPTIONS (DISTINCT ON INPUT GROUP SIZE = 8) ORDER BY g, v DESCindexed byg, 10M rows in 1.25M groups, one worker, median of three:TopK final consolidateReduced TopK inputWithout a hint the plan is unchanged (25.0s before and after; that case is dominated by seven bucket stages that cannot shrink eight-row groups, a separate question about the default bucket schedule). Measured on a local build with the other hydration PRs in flight applied.
test/sqllogictest/topk.sltpasses with the flag on; CI runs the whole suite with it on.Keying the input for the first stage once
The initial map hashed each row and packed
(hash, group), and the first stage's map immediately replaced the hash byhash % modulusand repacked. With a hint and no bucket stages the final stage's modulus is 1, so the hash was computed only to be replaced by zero. The initial map now keys rows for the first stage directly, and a stage rekeys only when its input is keyed for an earlier stage, as the min/max hierarchy already does. This is independent of the flag and removes oneFlatMapoperator (441ms on the measured shape) from every basic TopK dataflow; hinted topk goes from 4.73s to 4.21s.Byte slicing in the initial map
When the group key is a column prefix, the initial map now forms
(bucket, group)by pushing the bucket and appending the row's leading bytes, instead of unpacking the row and packing the group datums; other group keys keep the datum path. The map goes from 532ms to 240ms on the measured shape; hinted topk from 4.21s to 3.89s.This branch carries
RowRef::split_at_datumfrom #38665 as its own commit so it builds onmainalone; whichever PR lands first brings the helper.