[AIRADSW-735] ONNX control flow on CPU with MIGraphX branch execution on GPU - #63
Draft
urpetkov-amd wants to merge 5 commits into
Draft
[AIRADSW-735] ONNX control flow on CPU with MIGraphX branch execution on GPU#63urpetkov-amd wants to merge 5 commits into
urpetkov-amd wants to merge 5 commits into
Conversation
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.
Run ONNX control flow on CPU with MIGraphX branch execution on GPU
Summary
This PR adds an opt-in
cpu_control_flowmode to the AMDGPU/MIGraphX execution provider.When enabled, ONNX Runtime executes control-flow operators such as
Ifon the CPU while MIGraphX compiles and executes the branch bodies on the GPU. This follows the same high-level execution model used by DirectML: host-side control-flow orchestration with GPU-accelerated subgraphs.The original MIGraphX behavior remains the default. With
cpu_control_flow=0, MIGraphX continues to compile the control-flow operator and its nested subgraphs as one fused program.The change also adds the graph serialization and input-binding support required to compile control-flow branches as standalone MIGraphX programs without breaking the legacy fused path.
Motivation
The
lmx-v1-fp32-512x512model contains anIfwhose condition depends on a runtime scalar value (alpha). Under the original MIGraphX path, theIfis lowered into a MIGraphX control-flow operation.Although the branch computation runs on the GPU, selecting the branch requires MIGraphX to:
The generated program contains operations equivalent to
hip::copy_from_gpuandhip::sync_streamaround the condition. This introduces a blocking GPU-to-CPU synchronization in the middle of inference. Investigation of thelmxmodel showed this synchronization dominating execution time, with roughly 6 ms attributable to the condition transfer and stream wait.Execution model
Default mode:
cpu_control_flow=0If.If.New mode:
cpu_control_flow=1If,Loop, andScanare excluded from the top-level MIGraphX partition.GreaterandCastare also left on the CPU by the current partitioning policy so that a generated boolean condition remains CPU-resident.run_async.For the tested
Ifmodel, this changes the execution flow from:to:
Implementation
Control-flow-aware partitioning
MIGraphXExecutionProvider::GetCapability()now distinguishes between the top-level graph and a graph owned by a control-flow node.When CPU control flow is disabled:
When CPU control flow is enabled:
Compile().This produces separate MIGraphX programs for the selected branch bodies while ONNX Runtime retains ownership of control-flow execution.
Standalone branch input serialization
ONNX control-flow branch graphs can consume values from their parent graph without declaring those values as normal branch inputs. ONNX Runtime exposes these values as implicit or outer-scope inputs.
That representation works when the branch remains nested under an
If, but it is insufficient when the branch is extracted and serialized as a standalone model for MIGraphX compilation. A standalone model must declare every non-initializer external value as a graph input.GraphToProtoImpl()now promotes outer-scope values to graph inputs only for the root graph being serialized. Recursive serialization of nested control-flow graphs disables promotion.This distinction is required for both modes:
Ifserialization must preserve outer-scope captures.Complete input-name binding
The previous input mapping only covered the most direct fused-node inputs. Standalone branch programs can also depend on graph inputs and implicit inputs captured from the parent graph.
BuildInputNameIndices()constructs the kernel-context input mapping in this order:GetGpuInputData()now:Stream and HIP graph behavior
CPU control flow creates synchronization and ownership boundaries between ONNX Runtime's CPU execution and independently compiled MIGraphX branches.
To preserve correctness:
Result
This PR provides an opt-in execution strategy for ONNX control flow that avoids MIGraphX's expensive mid-inference GPU-to-host condition synchronization.
The final behavior is: