Conversation
Every worker sees keys from all over the scan, so each partial ends up about as wide as the answer and folding eight of them into the first is seven full width merges one after another, at the point in the query where nothing else is running. On the hundred thousand group bench that is 8 to 18 ms of a 65 ms query. Same seven merges, three rounds, each round on the pool the scan has just finished with. An odd table carries to the next round rather than joining a pair, so every merge in a round is the same size, and this thread takes one pair itself the way worker zero does on the scan.
The rows of a wide aggregation were built one after another on the thread that finished the run, and that is 7 to 17 ms of a 65 ms query at eight workers. Decoding is per group and the groups are settled once the order is, so slices of the order are independent work and the pool the scan just finished with is sitting idle. One hand per worker that ran, not one per core: a query asked for on one thread is answered on one thread, tail included, or the per core numbers in budgets.toml stop meaning what they say. Under four thousand groups a hand it stays on this thread, since a latch and a lock per hand cost more than the decoding they would split.
Bounded list entities from #770, which merged without them.
This was referenced Aug 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow on to #766, same query and same measurement. After that change the 100k group aggregation at eight workers spends its time as roughly 37 ms of parallel scan and 20 of serial tail, and the tail is two things: folding the eight workers' group tables into one, and building the answer rows out of the folded table.
Both are parallel work being done on one thread while the pool sits idle, and both are here.
The fold went pairwise up a tree. Every worker sees keys from all over the scan, because that is what the morsel scheduler is for, so each partial ends up about as wide as the answer and every fold is a full width merge. Folding eight of them into the first is seven of those one after another. The tree does the same seven merges in three rounds, each round on the pool. An odd table carries to the next round rather than joining a pair, so every merge in a round is the same size, and this thread takes one pair itself the way worker zero takes morsels.
The row build went to slices of the settled order. Once the order is decided the groups are independent, so a hand takes a slice and decodes it into finished rows. It reads the accumulators out of the table rather than moving them away first, which an accumulator being
Copymakes free, and that is what lets the slices share the table.Hands are per worker that ran, not per core. A query asked for on one thread has to be answered on one thread, tail included, or the per core floors in budgets.toml stop measuring a core. Under four thousand groups a hand it stays on this thread, because a latch and a lock per hand cost more than the decoding they would split.
There is a test that a table wide enough to take the split answers exactly what the same table answers on one hand, in the same order, since the split is a split of the work and not of the answer.
Measured on gamingpc, which was quiet, medians of six paired alternating runs of two separately built binaries. The hundred thousand group query went 40.8 ms to 33.9 at eight workers, 243 to 296 M rows/s, and the branch was faster in all six pairs. At one worker it read 99.5 against 99.4, which is the check that the hand count follows the run and not the machine. The twelve group shape and the thousand group string shape did not move, the first because it has no tail and the second because a thousand groups is under the split. That shape scales 2.9x from one worker to eight now, against 2.4x before.
Part of P2, zu#75.