You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Split out of #349, which is otherwise complete. #366 made the grouped vectorized path
parallel-aware and it works — but the planner declines it on exactly the shapes where
it wins most, and the reason is a costing asymmetry rather than anything about the
node.
Forced onto G1 the parallel arm runs in 1,184 ms against the serial node's 4,555 —
a 3.9x win the planner is declining.
Mechanism
The planner's own numbers on G1:
partialScan=15,042 ppath=16,292 gather=16,292 final=44,324 serial=20,042
dNumGroups=200,000 (actual groups ~8,000)
Our partial node under its Gather costs 16,292 against the serial node's 20,042
— it wins on everything it actually does. The core Finalize HashAggregate on top adds 28,031, and that term is priced off dNumGroups.
For a date_trunc(...) grouping key estimate_num_groups cannot estimate distinctness
and returns a count near the input row count: 200,000 estimated against ~8,000 actual
here, and 2,000,000 against 48,000 on a larger fixture. The finalize is overpriced by
that ratio and loses the comparison on its own.
Why it is structural, not a tuning miss
The serial node emits finished values, so it pays no finalize at all. #350
deliberately priced it per input row with no per-output-group term — an earlier version
charged per group and autoanalyze's group estimate flipped the plan choice, so the node
sometimes did not run.
So any two-phase plan pays a group-count-driven finalize and the serial node does not.
When the group estimate is inflated, the serial node wins by construction, whatever
the parallel arm actually costs.
The mechanism is confirmed, not inferred
G3's grouping key is a plain column, where estimate_num_groups is accurate, and there
the parallel arm is chosen and is faster. Accurate estimate → chosen; inflated
estimate → declined. Same code, same box, same session.
Fix directions
Charge the serial node for the group hash table it builds, so both arms pay a
group-count term. Closest to correct, and the reason fix: charge the grouped aggregate path for the folding it does (#349) #350 avoided it (autoanalyze
flipping the choice) is worth re-testing rather than assumed — it may have been an
artifact of charging per output group instead of per hash entry built.
Cost the finalize off something less brittle than estimate_num_groups on an
expression key — e.g. clamp it by the partial arm's own row estimate.
Leave it, and accept that the parallel arm only fires on plain-column keys.
(1) and (2) both change plan choice on shapes beyond this one, which is why #366 did
not fold either in: each deserves its own measurement.
Not urgent
pgcolumnar.enable_group_vectorization and enable_parallel_vector_agg are both off
by default, so no default install is affected. Post-alpha in my view; noted on #367.
Related
#349 (parent, complete), #366 (the parallel arm), #350 (the serial node's costing).
Summary
Split out of #349, which is otherwise complete. #366 made the grouped vectorized path
parallel-aware and it works — but the planner declines it on exactly the shapes where
it wins most, and the reason is a costing asymmetry rather than anything about the
node.
20M-row TSBS-shaped fixture, PG18 assert, 4 workers:
Forced onto G1 the parallel arm runs in 1,184 ms against the serial node's 4,555 —
a 3.9x win the planner is declining.
Mechanism
The planner's own numbers on G1:
Our partial node under its Gather costs 16,292 against the serial node's 20,042
— it wins on everything it actually does. The core
Finalize HashAggregateon top adds28,031, and that term is priced off
dNumGroups.For a
date_trunc(...)grouping keyestimate_num_groupscannot estimate distinctnessand returns a count near the input row count: 200,000 estimated against ~8,000 actual
here, and 2,000,000 against 48,000 on a larger fixture. The finalize is overpriced by
that ratio and loses the comparison on its own.
Why it is structural, not a tuning miss
The serial node emits finished values, so it pays no finalize at all. #350
deliberately priced it per input row with no per-output-group term — an earlier version
charged per group and autoanalyze's group estimate flipped the plan choice, so the node
sometimes did not run.
So any two-phase plan pays a group-count-driven finalize and the serial node does not.
When the group estimate is inflated, the serial node wins by construction, whatever
the parallel arm actually costs.
The mechanism is confirmed, not inferred
G3's grouping key is a plain column, where
estimate_num_groupsis accurate, and therethe parallel arm is chosen and is faster. Accurate estimate → chosen; inflated
estimate → declined. Same code, same box, same session.
Fix directions
group-count term. Closest to correct, and the reason fix: charge the grouped aggregate path for the folding it does (#349) #350 avoided it (autoanalyze
flipping the choice) is worth re-testing rather than assumed — it may have been an
artifact of charging per output group instead of per hash entry built.
estimate_num_groupson anexpression key — e.g. clamp it by the partial arm's own row estimate.
(1) and (2) both change plan choice on shapes beyond this one, which is why #366 did
not fold either in: each deserves its own measurement.
Not urgent
pgcolumnar.enable_group_vectorizationandenable_parallel_vector_aggare both offby default, so no default install is affected. Post-alpha in my view; noted on #367.
Related
#349 (parent, complete), #366 (the parallel arm), #350 (the serial node's costing).