Skip to content

[CUDA] Make GatherND capture-safe - #32120

Merged
Tianlei Wu (tianleiwu) merged 2 commits into
mainfrom
tlwu/20260816/gather_nd_capture_safe
Sep 16, 2026
Merged

Tianlei Wu (tianleiwu) merged 2 commits into
mainfrom
tlwu/20260816/gather_nd_capture_safe

Conversation

@tianleiwu

Copy link
Copy Markdown
Contributor

Description

GatherND's PrepareCompute staged the per-dimension slice strides through device memory with a cudaMemcpyAsync whose source was a std::vector local to the call. The copy outlives the vector. That is harmless for a normal launch, but not under CUDA graph capture: the recorded memcpy node keeps the host address and re-reads it on every replay, long after the vector is gone.

The slice offsets then come out as garbage and the gather reads far outside its input.

Summary of Changes

File Change
onnxruntime/core/providers/cuda/tensor/gather_nd.cc Build the slice strides in a TArray instead of a std::vector + device staging buffer; drop the scratch allocation and the async copy.
onnxruntime/core/providers/cuda/tensor/gather_nd_impl.cu Take the strides by value.
onnxruntime/core/providers/cuda/tensor/gather_nd_impl.h Update the launcher signature.

The array holds at most rank elements, which TArray::capacity already covers for input_dims on the same code path, so no new capacity assumption is introduced.

Testing

  • Existing GatherND CPU/CUDA parity tests pass.
  • The failure this fixes only reproduces under CUDA graph capture with a replay after the source vector's stack frame is gone; the fix removes the host dependency entirely, so the recorded graph no longer references any host memory for this operator.

Motivation and Context

This is a correctness fix for CUDA graph capture, not a performance change, though it does remove one allocation and one H2D copy per Run. It is one of a small set of EP-level issues that must be fixed before a graph-captured decode loop is safe; the companion issue in CudaAsyncBuffer is addressed separately.

Checklist

  • Tests added/updated (existing coverage exercises the changed path)
  • No breaking changes
  • Documentation updated (not applicable)

Copilot AI balanced review requested due to automatic review settings August 17, 2026 01:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Makes CUDA GatherND safe for graph capture and replay by eliminating stale host-memory references.

Changes:

  • Replaces host-vector staging with a by-value TArray.
  • Removes the scratch allocation and asynchronous H2D copy.
  • Updates launcher and kernel signatures accordingly.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
onnxruntime/core/providers/cuda/tensor/gather_nd.cc Builds and passes slice strides by value.
onnxruntime/core/providers/cuda/tensor/gather_nd_impl.cu Accepts by-value strides in CUDA code.
onnxruntime/core/providers/cuda/tensor/gather_nd_impl.h Updates the launcher declaration.

πŸ’‘ Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

PrepareCompute staged the per-dimension slice strides through device memory
with a cudaMemcpyAsync whose source was a std::vector local to the call.  The
copy outlives the vector, which is harmless for a normal launch but not under
CUDA graph capture: the recorded memcpy node keeps the host address and reads
it again on every replay, long after the vector is gone.  The slice offsets
then come out as garbage and the gather reads far outside its input.

Pass the strides by value in a TArray instead, the way input_dims already is.
The array is at most rank elements, which TArray::capacity already covers for
input_dims, and the copy disappears along with the scratch buffer.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟒 Approval recommended

The lifetime bug is removed cleanly and covered by a targeted capture-and-replay regression test.

Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@tianleiwu
Tianlei Wu (tianleiwu) merged commit 71b3346 into main Sep 16, 2026
95 of 96 checks passed
@tianleiwu
Tianlei Wu (tianleiwu) deleted the tlwu/20260816/gather_nd_capture_safe branch September 16, 2026 04:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants