Add experimental per-expression AST JIT for integral add and multiply - #15312
Add experimental per-expression AST JIT for integral add and multiply#15312thirtiseven wants to merge 17 commits into
Conversation
|
@greptile full review |
Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
Greptile SummaryThis PR introduces an experimental per-expression AST JIT path for
Confidence Score: 4/5Safe to merge with the feature disabled by default; the JIT path is only reachable with an explicit internal config, so production workloads are unaffected. The core resource management is correctly implemented and consistent with the rest of the codebase. Two non-blocking issues exist: restore() leaves completionRegistered unreset (functionally correct but subtle), and there is no code-level guard against the config being enabled without LIBCUDF_JIT_ENABLED=1, which would produce a hard runtime failure rather than a graceful fallback. Mixed-expression integration tests also only cover IntegerType. GpuAstJitExpression.scala — the restore()/completionRegistered interaction and the missing cuDF JIT availability guard are both worth addressing before the feature graduates from experimental. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Meta as GpuProjectExecMeta
participant Wrap as GpuAstJitExpression (object)
participant JIT as GpuAstJitExpression (instance)
participant Exec as GpuProjectExec
participant cuDF as cuDF JIT API
Meta->>Wrap: wrapProjectExpressions(gpuExprs)
Wrap-->>Meta: jitProjectList
Meta->>Exec: GpuProjectExec(jitProjectList)
Note over Exec: Task starts
Exec->>JIT: checkpoint()
JIT->>cuDF: convertToAst().compile()
cuDF-->>JIT: CompiledExpression
JIT->>JIT: register onTaskCompletion(close)
loop per batch
Exec->>JIT: columnarEval(batch)
JIT->>cuDF: computeColumnJit(table)
cuDF-->>JIT: ColumnVector
JIT-->>Exec: GpuColumnVector
end
alt GPU OOM
JIT->>JIT: restore() close expression
Exec->>JIT: retry getCompiledExpression recompile
end
Note over JIT: Task ends
JIT->>JIT: onTaskCompletion close()
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Meta as GpuProjectExecMeta
participant Wrap as GpuAstJitExpression (object)
participant JIT as GpuAstJitExpression (instance)
participant Exec as GpuProjectExec
participant cuDF as cuDF JIT API
Meta->>Wrap: wrapProjectExpressions(gpuExprs)
Wrap-->>Meta: jitProjectList
Meta->>Exec: GpuProjectExec(jitProjectList)
Note over Exec: Task starts
Exec->>JIT: checkpoint()
JIT->>cuDF: convertToAst().compile()
cuDF-->>JIT: CompiledExpression
JIT->>JIT: register onTaskCompletion(close)
loop per batch
Exec->>JIT: columnarEval(batch)
JIT->>cuDF: computeColumnJit(table)
cuDF-->>JIT: ColumnVector
JIT-->>Exec: GpuColumnVector
end
alt GPU OOM
JIT->>JIT: restore() close expression
Exec->>JIT: retry getCompiledExpression recompile
end
Note over JIT: Task ends
JIT->>JIT: onTaskCompletion close()
Reviews (1): Last reviewed commit: "Update copyright years" | Re-trigger Greptile |
Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
|
Java binding is in 26.10, waiting for main branch to switch... |
Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
|
NOTE: release/26.08 has been created from main. Please retarget your PR to release/26.08 if it should be included in the release. |
Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
Signed-off-by: Haoyang Li <haoyangl@nvidia.com>
Related to #15069.
Description
This draft PR proposes a minimal plugin-side integration framework for using cuDF AST JIT within
GpuProjectExec. It is intended as an architectural proposal and an experimental foundation for future AST JIT work, rather than as a generally beneficial performance feature in its current form.The implementation intentionally supports only non-ANSI
IntegerTypeandLongTypeaddition and multiplication. Other operators and data types are not enabled for AST JIT by this PR.It adds the internal
spark.rapids.sql.projectAstJitEnabledconfiguration, which is disabled by default. When enabled, the plugin recursively identifies maximal subtrees composed of the supported add and multiply expressions and wraps them inGpuAstJitExpression.Expressions outside those supported subtrees continue to use the normal GPU Project evaluation path. This allows, for example, an add or multiply subtree to use AST JIT while an enclosing subtraction continues to use the existing GPU expression implementation. JIT and non-JIT expressions can also coexist as separate outputs of the same
GpuProjectExec.The main purpose of this PR is to establish and review the plugin/JNI integration boundary, per-expression selection and fallback behavior, retry handling, resource ownership, configuration, and plan visibility. It does not implement the following planned cuDF optimizations:
Performance
The primary benchmark reads 100 million rows from 16 Parquet partitions, evaluates eight distinct
INT/BIGINTProject expressions with 16 alternating add and multiply operators each, and consumes every output with an aggregate. Steady-state modes use one discarded warmup and five measured iterations. Cold-state modes use three fresh Spark processes.Environment: Spark 3.5.2 with
local[8], NVIDIA RTX 5880 Ada Generation, CUDA/NVRTC 12.8, andLIBCUDF_JIT_ENABLED=0. AST JIT is enabled only throughspark.rapids.sql.projectAstJitEnabled=true.Benchmark scripts: Project AST JIT benchmark gist
All modes produced the same row count and aggregate checksum. Cold-state results intentionally include the time-to-first-result penalty. For this workload, hot AST JIT is effectively at parity with legacy AST, while non-warm JIT modes are substantially slower because of compilation cost.
An additional exploratory benchmark used one expression with 64 alternating add and multiply operators, five discarded warmups, and two independent applications with 20 measured iterations each:
This indicates that expression-specific JIT code can help sufficiently deep expressions, but the current results do not demonstrate a broad performance benefit for typical expressions. Spark Project operation metrics are not included because they measure host-side operator time and do not reliably attribute asynchronous CUDA kernel execution; E2E time is used for the comparison.
Checklists
Documentation
Testing
Performance