Fix a regression in graph-capture session initialization that rejects an empty graph#29457
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a regression in InferenceSession::Initialize() where enabling graph capture for EPs using the ALLOW_CPU_FOR_SHAPES policy incorrectly rejected models that optimize/fold down to an empty graph. This restores expected behavior for constant-only / fully-fused graphs while keeping the existing validation for non-empty graphs.
Changes:
- Allow empty graphs to pass
AreAllComputeNodesAssignedToEpOrCpu()as long as there are no Memcpy nodes. - Add a DirectML regression test that enables DML graph capture on a Constant-only model and asserts session creation does not throw.
Show a summary per file
| File | Description |
|---|---|
| onnxruntime/test/shared_lib/test_inference.cc | Adds a DML regression test for graph-capture session initialization on a model that folds to an empty graph. |
| onnxruntime/core/session/inference_session.cc | Updates the graph-capture eligibility check to allow empty graphs under ALLOW_CPU_FOR_SHAPES when no Memcpy nodes exist. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Low
tianleiwu
reviewed
Jul 1, 2026
tianleiwu
left a comment
Contributor
There was a problem hiding this comment.
I reviewed the current head. The graph-capture eligibility change looks correct for optimized-empty graphs, and the DML run/capture hooks make the empty replay path a no-op. I found one small style nit in the test updates.
Contributor
Author
|
@tianleiwu thanks for taking a look; would you mind approving if it meets your bar? Thanks! |
tianleiwu
approved these changes
Jul 6, 2026
This was referenced Jul 11, 2026
This was referenced Jul 15, 2026
Open
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.
Description
InferenceSession::Initialize() validates, for any EP that has graph capture enabled and uses the ALLOW_CPU_FOR_SHAPES node-assignment policy (DirectML, WebGPU), that the partitioned graph is eligible for capture via AreAllComputeNodesAssignedToEpOrCpu(). That helper currently requires at least one node to be assigned to the EP:
return has_node_on_provider && !HasMemcpyNodes(graph);
For a graph with no nodes, like a model that is fully consumed by the EP's runtime graph fusion, or that trivially folds away (like a lone Constant node), has_node_on_provider is false, so the session throws:
This session cannot use the graph capture feature as requested by the user as all compute graph nodes have not been partitioned to the DmlExecutionProvider
An empty graph contains nothing that violates the "all compute on the EP or CPU, no Memcpy" requirement, so it should be allowed. This change permits the empty-graph case:
return (has_node_on_provider || graph.NumberOfNodes() == 0) && !HasMemcpyNodes(graph);
Behavior is unchanged for all non-empty graphs (the expression collapses to the original has_node_on_provider).
Adds CApiTest.DmlGraphCaptureEmptyGraph : creates a DML session with ep.dml.enable_graph_capture=1 on a single Constant model (folds to an empty graph) and asserts session creation does not throw. The test fails without this fix and passes with it.
Motivation and Context
#27958 refactored the graph-capture selection logic and replaced AreAllComputeNodesAssignedToCudaOrJsOrDmlEpWebGpuEp() (which returned true for an empty graph) with AreAllComputeNodesAssignedToEpOrCpu() and its new has_node_on_provider requirement. The behavior verified with a DLL-swap: onnxruntime-directml 1.24.x/1.25.2 create the session successfully; 1.27 throws.
The failure surfaces through ONNX Runtime GenAI on DirectML: GenAI creates a per-device "allocator-initialization" session on a Constant-only model with DML graph capture enabled to obtain a device allocator (model.cpp). Under ORT 1.27 that session throws at construction, which breaks every GroupQueryAttention-based GenAI LLM on the DirectML EP (observed as WinML EP certification failures across DeepSeek/Llama/Qwen/Phi models