fix(backend): detect failed CUDA kernel launches instead of returning zeros - #1175
Open
yingjerkao wants to merge 1 commit into
Open
fix(backend): detect failed CUDA kernel launches instead of returning zeros#1175yingjerkao wants to merge 1 commit into
yingjerkao wants to merge 1 commit into
Conversation
… zeros
A kernel launch reports its status only through cudaGetLastError(); the
launch expression itself cannot fail visibly. Nothing in the GPU backend
read that status, so a launch that never ran left the output buffer holding
whatever it already contained -- usually zeros from a fresh allocation --
and the failure was indistinguishable from a numerical bug.
Demonstrated on an sm_89 GPU with a library built for 75-real (SASS with no
JIT-able PTX), the same tree with and without this change:
before: linalg::Add -> values = 0 0 0 0, no error
after: linalg::Add -> CytnxError "CUDA kernel launch failed for
'tn_kernel': named symbol not found" at cuArithmeticDispatch.cuh:122
Add CYTNX_CHECK_CUDA_LAUNCH and call it after every kernel launch -- 213
sites across 21 files. It reads the status without synchronizing, so it
stays cheap enough to sit after each launch, and it catches launch-time
failures (no image for the running device, bad launch configuration) rather
than errors raised during execution, which surface asynchronously. Raising
through cytnx_error_msg keeps the failure catchable and reports the launch
site, unlike checkCudaErrors, which exit()s the process.
The pre-existing checkCudaErrors(cudaGetLastError()) in cuTrace_internal.cu
becomes the same macro rather than a second read that could only ever see
success. cuTNPerm_gpu.cu is left alone: it is not referenced by any build
file (#1142).
Note one trade-off: 30 of the 213 checked launch sites are followed by
cudaFree of temporary device buffers (114 free calls in all), which a throw
now skips, so a failed launch leaks device memory where it previously
continued silently. An exception plus a leak is preferable to silently wrong
numbers, and scoping those buffers with RAII is left as a follow-up rather
than mixed into this change.
Relates to #1171.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1175 +/- ##
=======================================
Coverage 67.92% 67.92%
=======================================
Files 208 208
Lines 22354 22354
Branches 72 72
=======================================
Hits 15184 15184
Misses 7148 7148
Partials 22 22
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
This was referenced Aug 4, 2026
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.
Problem
A CUDA kernel launch reports its status only through
cudaGetLastError()— thelaunch expression itself cannot fail visibly. Nothing in the GPU backend read
that status, so a launch that never ran left the output buffer holding whatever
it already contained, usually zeros from a fresh allocation. The failure was
indistinguishable from a numerical bug.
This is part 2 of #1171: on an sm_80 card, a default-architecture build returned
zeros across the entire GPU surface — including tests unrelated to any local
change — with no error raised. As that issue puts it, a launch-status check
turns "your kernel is wrong" into "no kernel image for this device".
Fix
Add
CYTNX_CHECK_CUDA_LAUNCH(include/cytnx_error.hpp, inside theUNI_GPUguard) and call it after every kernel launch in the built GPU sources — 213
sites across 21 files.
Design notes:
after every launch. That scopes it to launch-time failures (no image for the
running device, bad launch configuration) rather than errors raised during
execution, which surface asynchronously at the next synchronizing call.
cytnx_error_msg, so the failure is catchable and names thekernel plus the launch site — unlike
checkCudaErrors, whichexit()s theprocess.
checkCudaErrors(cudaGetLastError())incuTrace_internal.cubecomes the same macro rather than a second read thatcould only ever see success.
cuTNPerm_gpu.cuis deliberately left alone: it is not referenced by any buildfile (Delete cuTNPerm_gpu.{cu,hpp}: orphaned source, never compiled and would not compile #1142).
Some
if/elsebranches gained braces because the check becomes a secondstatement; that is the only other change in the diff.
Trade-off worth a reviewer's attention
30 of the 213 checked launch sites are followed by
cudaFreeof temporarydevice buffers (114 free calls in all), which a throw now skips — so a failed
launch leaks device memory where it previously continued silently. An exception
plus a leak beats silently wrong numbers, but scoping those buffers with RAII is
a genuine follow-up rather than something to mix into this change.
Tracked as #1176, with the affected sites enumerated. The
DeviceBuffer<T>typethat fixes it already exists (added by #1146 for the same failure mode in the
cuSOLVER wrappers), so the follow-up is reuse rather than new machinery.
Testing
GPU suite:
ctest -L gpuon 2x RTX 4070 Ti SUPER (sm_89, arch 89 build) —841/841 passed, 0 failed, 1726 s. The 4 non-running tests are pre-existing
source-level
GTEST_SKIP()s (GPUExpMunimplemented, cuQuantum-dependent QR,zero-heavy SVD). This is the no-false-positives evidence: the guard stays quiet
across the whole GPU surface when launches genuinely succeed.
Guard fires correctly: a standalone probe against the real header confirms
a valid launch does not throw, while a launch rejected by the driver (2048
threads/block, over the 1024 limit — the same launch-rejected class as "no
kernel image for this device") throws a catchable
cytnx::error:CUDA kernel launch failed for 'probe_kernel': invalid argument.End-to-end arch mismatch, from the session that wrote the patch: same tree,
library built for
75-real(SASS with no JIT-able PTX), run on sm_89.Formatting:
pre-commit run --files <changed>clean (clang-format v14).CPU presets not run, by design: the only non-
.cu/.cuhchange is inside#if defined(UNI_GPU), so this commit cannot affect a CPU build. CI'sci-cmake_testscovers that gate.No automated regression test accompanies this. Reproducing the bug requires
building the whole library for an architecture the running device cannot execute,
which is not something the test suite can set up — hence the manual
demonstration above.
Scope
Relates to #1171; does not close it. Part 1 of that issue — the multi-arch
CMAKE_CUDA_ARCHITECTURESdefault emitting LTO IR that is never device-linked —is untouched and still wants a fix.
🤖 Generated with Claude Code