gengliangwang opened a new pull request, #58220:
URL: https://github.com/apache/spark/pull/58220

   ### What changes were proposed in this pull request?
   
   This backports #57059 ([SPARK-57980][SQL]) to `branch-4.3`, cherry-picked 
from master commit
   c62a58d77ab45ffc5276845d050b5b1f0d7fee8f. The PR is already on `master` and 
`branch-4.x`.
   
   When `HashAggregateExec` cannot allocate an aggregation buffer from the 
in-memory hash map, its
   generated code spills the map to disk: the first spill destructs the map 
into a new
   `UnsafeKVExternalSorter`, and later spills merge the map into the existing 
sorter. This
   type-independent branch is re-emitted into every HashAggregateExec 
whole-stage-codegen stage.
   
   This PR extracts it into a shared helper
   `HashAggregateExec.spillHashMapToSorter(hashMap, sorter): 
UnsafeKVExternalSorter` and calls it from
   the generated code. Returning the sorter lets the generated code keep it in 
a single mutable field.
   
   Before (generated code, per HashAggregateExec stage):
   ```java
   if (hashAgg_unsafeRowAggBuffer_0 == null) {
     if (hashAgg_sorter_0 == null) {
       hashAgg_sorter_0 = hashAgg_hashMap_0.destructAndCreateExternalSorter();
     } else {
       
hashAgg_sorter_0.merge(hashAgg_hashMap_0.destructAndCreateExternalSorter());
     }
     ...
   ```
   
   After:
   ```java
   if (hashAgg_unsafeRowAggBuffer_0 == null) {
     hashAgg_sorter_0 = 
org.apache.spark.sql.execution.aggregate.HashAggregateExec
       .spillHashMapToSorter(hashAgg_hashMap_0, hashAgg_sorter_0);
     ...
   ```
   
   The helper is a static method (it needs nothing from the plan instance), so 
-- unlike routing
   through the `references[]` plan object -- it adds no per-stage constant-pool 
entry. Only the fully
   type-independent spill-and-create block is moved. The surrounding logic that 
is interleaved with the
   test-only controlled-fallback counter (`resetCounter`) and the retry lookup 
(which uses the
   schema-specific key/hash variables) is left inline unchanged.
   
   **Conflict resolution.** The cherry-pick conflicted in 
`HashAggregateExec.scala`. On `master`,
   `findOrInsertRegularHashMap` has since been restructured by the 
adaptive-partial-aggregation work,
   which split the spill block out into its own `spillMap` fragment reused by 
two branches. `branch-4.3`
   predates that and still has the single flat interpolated string with the 
spill block inline. The
   resolution keeps `branch-4.3`'s structure and replaces only the inline
   `destructAndCreateExternalSorter`/`merge` block with the helper call; no 
part of the
   adaptive-partial-aggregation refactor is pulled in. The new helper and the 
new test suite are
   verbatim from the master commit.
   
   ### Why are the changes needed?
   
   This is part of the umbrella SPARK-56908 (reduce the size of code generated 
by whole-stage codegen).
   The spill branch is real bytecode and constant-pool method-references (the
   `destructAndCreateExternalSorter` and `merge` calls) that Janino cannot fold 
away; collapsing it to a
   single helper call compiles it once per JVM instead of re-emitting it into 
every HashAggregateExec
   stage. Planning all 135 TPC-DS queries produces 562 HashAggregateExec 
whole-stage-codegen stages that
   carry this block.
   
   On `master`, `WholeStageCodegenSizeBenchmark` measured this as -0.4% summed 
constant pool
   (432,881 -> 431,233) and -0.2% source code size (21,846,001 -> 21,794,526 
chars), with max method
   bytecode, inner classes, and codegen fallbacks unchanged. Those numbers are 
not re-measured here: the
   benchmark harness (SPARK-57915) is not on `branch-4.3`.
   
   Keeping `branch-4.3` in sync with `master` and `branch-4.x` on this file 
also keeps subsequent
   SPARK-56908 backports conflict-free.
   
   ### Does this PR introduce _any_ user-facing change?
   
   No. The helper performs exactly the same spill/merge operations in the same 
order as the previous
   inline code; this is a pure code-organization change with no behavioral 
difference.
   
   ### How was this patch tested?
   
   Existing tests, plus the test suite that came with the original PR, run on 
`branch-4.3`:
   - `HashAggregateExecSuite` (added by the original PR) -- directly covers 
both branches of the
     extracted helper, the first-spill destruct and the later-spill merge. 2 
tests, passed.
   - `HashAggregationQueryWithControlledFallbackSuite`, which forces the hash 
map to spill at
     controlled points (`fallbackStartsAt` = 1, 2, 3) and therefore exercises 
the rewritten generated
     code with whole-stage codegen both on and off. 26 tests, passed.
   - `sql/compile` + `sql/Test/compile` clean.
   
   No behavior changes, so no new tests are added beyond the ones cherry-picked 
with the commit.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Claude Code (Opus 5)
   


-- 
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