Asynchronous errors: find the failing work, not just the reporting call
Separate API and launch checks from execution completion, then use scoped synchronization and sanitizer evidence to narrow a device fault.
On this page
The question for today
What you’ll understand
- Explain why successful host submission does not prove successful device execution.
- Place diagnostic observation points around the suspected kernel.
- Choose memcheck, racecheck, initcheck, and synccheck within their documented scopes.
A GPU fault can be detected after the host has already submitted later work. The next synchronization or API call that reports an asynchronous error may therefore be an observation point rather than the origin of the bug. Debugging starts by separating request validation, launch status, device execution, and completion. Use CUDA12.6 runtime semantics for those boundaries, and record the actual installed Compute Sanitizer version and supported hardware when collecting diagnostic evidence.
Check both submission and execution boundaries
Check every relevant CUDA API return value. After a kernel launch, inspect the documented last-error state with an appropriate call, understanding that cudaGetLastError clears that state while cudaPeekAtLastError does not. These calls do not wait for the launched kernel to finish, and an error they observe can also reflect earlier asynchronous work. A clean launch-side check is therefore not proof that the device executed every access correctly. Start from a known diagnostic boundary and avoid ignoring a prior error that contaminates the interpretation.
For diagnosis, synchronize the relevant stream or completion event immediately after the suspected work and check that result. This narrows the interval in which an execution failure could have arisen, but earlier outstanding work or context-wide consequences still require care. Broadly serializing execution can change a race’s schedule, so a bug disappearing under debug synchronization is useful evidence, not proof of correctness. Preserve enough context to compare the original and instrumented schedules.
Choose the tool that covers the suspected mechanism
Compute Sanitizer memcheck can identify invalid or misaligned accesses and related memory errors within its documented coverage. Racecheck targets supported shared-memory access hazards rather than proving all global-memory protocols correct. Initcheck targets supported uninitialized device-memory reads, and synccheck targets misuse of supported synchronization primitives. Run memory checks before interpreting synchronization reports when appropriate, because earlier memory corruption can obscure later behavior. Include line information and preserve the exact kernel configuration and input needed to reproduce the issue.
A clean tool run covers the exercised execution and the tool’s supported checks; it is not a proof about every input, architecture, or schedule. Some asynchronous faults leave a context unusable for continuing work, so clearing a last-error variable is not a repair for corrupted device execution. Follow the documented recovery guidance for the error, often restarting the affected process or context as required. Keep the first causal diagnostic evidence rather than flooding logs with secondary failures from later operations.
Reduce the failing schedule without losing its cause
Reproduce with the same seed, dimensions, data types, stream mode, and allocation lifetimes. Minimize the input and kernel sequence while preserving the failure. Check tails, strides, pointer arithmetic, dynamic shared-memory size, barrier participation, and cross-stream reuse. Put a completion check after kernel A and then after B to narrow the first failing interval, while recording that the instrumentation changes scheduling. A late memcpy reporting an error does not justify blaming that copy if an earlier kernel already wrote outside its buffer.
The sampling lab illustrates only how a stated observation model changes with trial count. Reset, set p=0.5 and n=20, predict mean proportion 0.5 and variance 0.0125, then Step. Playback highlights exact binomial outcomes rather than running random GPU trials. The lab has no sanitizer, fault timing, or race scheduling, and real failures need not be independent Bernoulli events. Use it as a warning that repeated clean runs need explicit coverage assumptions, then rely on concrete bounds and diagnostic traces for this kernel.
Work it through
A later check reports an earlier bounds error
Kernel A processes 1,000 elements with four blocks of 256 threads but omits its i<N guard. Kernel B follows, and a later stream synchronization reports an execution error. Assume A attempts one write per launched thread.
A launches 1,024 threads, so 24 have indices outside the valid 0 through 999 range. In block three, local thread 232 maps to 1,000, the first invalid index.
A successful launch-side status check does not establish valid execution. The later synchronization can report the effect of A’s invalid access even though B was submitted more recently.
Add a checked diagnostic completion boundary after A and run an appropriate memory-checking tool on the reproducer. Repair the bounds/participation logic, then retest the original asynchronous schedule rather than keeping a global synchronization as a substitute for the fix.
The arithmetic identifies 24 attempted out-of-range writes. The reporting call alone does not identify the faulty kernel, and successful enqueueing does not validate memory safety.
The living notebook
Explore the model
Interactive lab · sampling
Repeated observations need a model
Reset, set p=0.5 and n=20, predict mean proportion 0.5 and variance 0.0125, then Step. These are exact independent-binomial outcomes, not sampled GPU tests, fault injection, sanitizer results, or a race detector.
- Mean sample proportion
- 0.5
- Sample-mean variance
- 0.0125
- Uniform CDF error bound
- ≤ 0.1062
Exact binomial masses for the mean of n independent Bernoulli trials, compared with normal mass between adjacent half-count boundaries; normal tails outside the finite support remain outside the bars. Playback highlights outcomes, not random samples. The Berry–Esseen bound applies to standardized CDFs, not individual bars or continuity-corrected errors. At p = 0 or 1, normal comparison and that bound are disabled. Normal-CDF evaluation uses a numerical approximation with absolute error below 7.5×10⁻⁸ (at most 1.5×10⁻⁷ per interval, before rounding). Parameter changes pause playback and recompute the model at the current elapsed time. Reset restores both parameters and returns time to zero. Playback starts paused. Adjust either parameter, then choose Play or Step.
Look forA clean repeated observation needs a coverage argument; it cannot replace a concrete memory and synchronization proof.
Pause & reason
Check your understanding
Your turn
Try it yourself
A kernel maps one thread per value for N=513 with B=256 and no guard. Compute blocks, launched threads, extra threads, and the first invalid thread in the final block. Does synccheck alone certify these writes?
Take a moment to make a prediction or work it out on paper. Open a hint when you need one.
Give me a hint
Round up, then subtract the valid range and convert the first invalid global index to its local index.
Compare with the solution
Three blocks launch 768 threads, leaving 255 out-of-range threads. The final block starts at 512, so local thread one maps to 513 and is first invalid. Synccheck targets synchronization misuse, not general memory-access safety; use an appropriate memory check and fix the bounds logic.
Keep the idea
Take it with you
- The API reporting an asynchronous error may not be its origin.
- Launch checks and completion checks provide different evidence.
- Sanitizer tools have specific scopes, and cleared error state is not repaired execution.
Read further
Original sources and deeper explanations to follow at your own pace.
Optional progress, saved in this browser when available. Revisit any lesson at any time.
Lesson not marked complete.