Repository navigation
[CUDA] Fix same-buffer copy abort on retained in-memory initializers (#29713) - #29792
Conversation
…icrosoft#29713) On the CUDA EP, a copy scheduled for a retained in-memory-external OrtValue initializer can have a source and destination that are the same physical host buffer. The host->host branch of GPUDataTransfer::CopyTensor/CopyTensorAsync failed ORT_ENFORCE(dst_data != src_data) on such a self-copy (hard abort), and inside a CUDA-graph capture region its cudaStreamSynchronize was illegal (CUDA 900 -> 901, invalidating capture). Early-return the no-op self-copy at the top of the host->host branch in both methods, before the HOST_ACCESSIBLE stream sync (the key to keeping capture valid). This matches CPUDataTransfer::CopyTensor and the existing device->device guard. Adds a CUDA EP internal regression test (gpu_data_transfer_test.cc) covering the sync, async, and cuda-graph capture cases. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
Tianlei Wu (tianleiwu)
left a comment
There was a problem hiding this comment.
Nice, focused fix. The key correctness point is right: in CopyTensorAsync the self-copy early-returns before the HOST_ACCESSIBLE cudaStreamSynchronize, which is exactly what keeps a CUDA-graph capture valid (avoids 900 → 901), and it matches CPUDataTransfer::CopyTensor and the existing device→device dst_data != src_data guards. Since the pre-fix path ORT_ENFORCE-crashed on dst == src, there was never any working behavior to preserve, so this strictly removes a failure path with no regression to legitimate copies.
The regression test is thorough — it covers sync, async, and (most importantly) the async self-copy inside a capture region, asserting cudaStreamEndCapture == cudaSuccess.
Only two optional test-hardening nitpicks below; neither blocks. LGTM.
|
@microsoft-github-policy-service agree |
… tests
Make the CreatePreferredAllocators() ordering assumption explicit by asserting the
pinned allocator is HOST_ACCESSIBLE (and the stream's GPU allocator is GPU/DEFAULT),
so the tests fail loudly instead of silently exercising the wrong branch if the
ordering ever changes. Also reword the tensor-size comment: these tests call
CopyTensor{,Async} directly and never go through the retention/threshold path, so the
size does not affect which branch is exercised.
Addresses review nitpicks from @tianleiwu on microsoft#29792.
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Thanks for the review, Tianlei Wu (@tianleiwu)! Addressed both nitpicks in 5919f6b: Added explicit assertions that the pinned allocator is HOST_ACCESSIBLE (and the stream's GPU allocator is GPU/DEFAULT) in all three tests, so they fail loudly rather than silently exercising the wrong branch if CreatePreferredAllocators() ordering ever changes. |
45053e8
into
microsoft:main
Description
Fixes #29713.
On the CUDA EP, a device copy scheduled for a weight retained as an in-memory-external
OrtValueinitializer can have a source and destination that are the same physical host buffer. The host->host branch ofGPUDataTransfer::CopyTensor/CopyTensorAsyncdid not handle this:ORT_ENFORCE(dst_data != src_data)fired -> hard failure.HOST_ACCESSIBLEcudaStreamSynchronizeis illegal during capture ->CUDA 900->cudaStreamEndCaptureCUDA 901, invalidating the capture.This became reachable once #29158 (initializer retention) landed.
Fix
Early-return the no-op self-copy at the top of the host->host branch in both methods, before the
HOST_ACCESSIBLEstream sync (the key to keeping capture valid). This matchesCPUDataTransfer::CopyTensorand the existing GPU->GPU guard.Testing
Adds a CUDA EP internal regression test (
gpu_data_transfer_test.cc) covering sync, async, and cuda-graph-capture self-copies. Verified failing before the fix (ORT_ENFORCE / CUDA 900->901) and passing after, on an RTX 3050 with a CUDA 12.8sm_86build.