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

   ### What changes were proposed in this pull request?
   
   Terms:
   - The compile cache (`CodeGenerator.cache`) keeps each compiled generated 
class by its source, so the same code is compiled once.
   - A trial compile (`CodeCompiler.trial`) is how whole-stage codegen decides 
whether to split a stage's expressions (SPARK-33301). It compiles the stage's 
code in one piece, reads each method's bytecode size, and holds back what the 
compile reports (the codegen metrics, the "Code generated in" log line and the 
reports of methods past the JIT limit) until it knows which code it keeps.
   
   Before this PR, the method sizes and the held reports were collected only 
while a class was being compiled. A trial that found the class already in the 
cache got neither, so `WholeStageCodegenExec.trialCompile` dropped the class 
from the cache and compiled it again. That happens when the class is cached but 
the split decision for its code is not: the decision was evicted, the same code 
ran earlier with the split off or under another limit, or another thread 
compiled it first.
   
   This PR keeps the sizes and the reports with the class in the cache:
   - Every compile into the cache runs under a trial of its own 
(`CodeCompiler.held`). The cache keeps the class together with what that 
compile held: the size of each method, and its reports in the order they were 
made (`CodeCompiler.HeldCompile`).
   - Inside a trial, `CodeGenerator.compile` gives the trial what the class's 
compile held, whether it compiled the class or found it cached 
(`TrialCompile.holdAll`). So a trial that keeps code it found cached still 
reports it, as it did when it compiled the class again.
   - `trialCompile` no longer drops and recompiles the class on a hit.
   
   Outside a trial, a compile's reports are now made when the compile ends 
instead of as they come, on the calling thread. With the JDK backend that is no 
longer the compile worker thread, which has no task context or session conf.
   
   ### Why are the changes needed?
   
   A trial that found its class cached compiled the stage's code a second time, 
only to measure sizes the first compile had already measured. Dropping the 
class could also make another thread's `doExecute`, which was about to use it, 
compile it again. A thread that waited for another thread's compile of the same 
class got no sizes at all, so the stage's decision was put off to a later run.
   
   For example, in one session:
   
   ```scala
   val branches = (0 until 300).map(i => s"WHEN id = $i THEN id * 
$i").mkString(" ")
   val q = s"SELECT CASE $branches ELSE -1 END AS r FROM range(100)"
   spark.conf.set("spark.sql.codegen.wholeStage.splitExpressions", "false")
   spark.sql(q).collect()  // compiles the stage's code in one piece
   spark.conf.set("spark.sql.codegen.wholeStage.splitExpressions", "true")
   spark.sql(q).collect()  // the trial finds that class in the cache
   ```
   
   On master, the second `collect()` drops the cached class and compiles it 
again before it decides to split. With this PR it decides from the sizes kept 
in the cache.
   
   ### Does this PR introduce _any_ user-facing change?
   
   No. Answers and generated code are the same, and the code a stage keeps 
makes its metric updates and reports as before. Two small differences in what 
is logged:
   - When a trial keeps code it found in the cache, its "Code generated in" 
line gives the time of that class's earlier compile.
   - With the JDK backend on an executor, a method past the JIT limit is 
reported at INFO, as `CodeCompiler.logHugeMethod` intends for executors. 
Before, the report ran on the compile worker thread, which has no task context, 
so it could be the once-per-JVM warning.
   
   ### How was this patch tested?
   
   New test in `WholeStageCodegenSuite`, "SPARK-60049: a trial that finds the 
class in the compile cache does not compile it". Under both Janino and the JDK 
backend, it compiles a 300-branch stage's code outside a trial, then runs 
`trialCompile` on the same code. It checks that the trial gets the method 
sizes, that the cache still holds the same class instance (on master it holds a 
new one, compiled again), and that the trial's held reports include the method 
past the JIT limit.
   
   The existing tests of the trial's reports in `WholeStageCodegenSuite` still 
apply. "SPARK-33301: the code a stage keeps unsplit reports its method past the 
JIT limit" goes through a trial that finds its class cached.
   
   CI: GitHub Actions on my fork.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Kiro (Claude Opus 5.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