Personalize this lesson
Adapt explanations and teaching visuals to your background and preferred voice.
Imagine reviewing a generated CUDA extension. Its author reports a 1.4x speedup on one H100 shape, [2, 4096]. You change the row width to 1009 and the numerical test fails. You restore the width but transpose the input, and it fails again. What has the speedup established? Only that one timed call looked promising. It hasn't established the operator's accepted domain.
Generated source needs the same scrutiny as other unfamiliar code. Models can reason about kernels and use compiler or device tools, but a plausible explanation doesn't establish what the loaded binary computes. Require a precise contract, independent tests, scoped runtime checks, fair measurements, and a recovery plan. Each supplies evidence that the others can't replace.
The GPU Runtime Execution Lab made host submission, device streams, static storage, fallback paths, and timing scopes explicit. Here, inspect the candidate behind that dispatch and build evidence for its correctness and safety before promotion.
One realistic operator carries this process throughout the chapter: weighted root mean square normalization (RMSNorm), weighted_rmsnorm(x, weight, eps). It rescales each row across its last dimension and applies a learned feature weight. For an example row [3.0, 4.0], the mean square is . Divide each entry by , then multiply by its corresponding weight. The positive scalar ensures an all-zero row never divides by zero.
For row index , feature index , and row width :
The reference implementation is pure PyTorch. A candidate may use CUDA, Triton, or compiler code. The evidence goals stay the same, while language/tool-specific checks can differ. The executable examples run on CPU and expose limitations in evaluator logic before you test a device implementation.
AI kernel generation dynamics and common failure modes
Three concrete defects are worth constructing in a test, rather than assuming a generator will avoid them:
- Missing ordering: A CUDA shared-memory consumer needs the appropriate synchronization with its producers. A race can occur within one block on one SM; it doesn't require 132 SMs, clock changes, or a busy GPU. Triton normally manages dependencies for its generated execution, so don't mechanically insert CUDA-style barriers into its source. Inspect the language and generated-code contract.
- Incorrect tails: A global-load mask doesn't establish that reduction padding contains the additive identity. Test widths around tile boundaries, including 127, 128, 129, and 1009. Separately check whether a shared-memory index stays in its allocation. Uninitialized in-bounds data and out-of-bounds access are different defects.
- Wrong address mapping: Correctness can survive a poor access pattern. For 32 aligned FP32 lane loads, contiguous addresses span four 32-byte sectors; a sufficiently spaced pattern can touch 32. Those are warp address counts, not a prediction of HBM bytes or runtime. Cache state, reuse, active lanes, and instruction layout matter. A small cached test can be a valid measurement for that workload while failing to represent a large streaming workload.
Write the operator contract before the prompt
Prompt engineering can't repair an underspecified operation. A code generator needs both the exact mathematical formula and the library contract surrounding it. PyTorch custom operators declare mutation, aliasing, and schemas via torch.library.custom_op, while torch.library.opcheck verifies registration, fake-tensor integration, and compilation mechanics rather than numerical correctness.[1] Treat those as distinct obligations.
Here is the contract for the running operator:
| Contract axis | weighted_rmsnorm requirement | Rejection example |
|---|---|---|
| Shape | x has shape [..., H], H > 0; empty outer dimensions are valid; weight has shape [H]; output shape matches x | Candidate assumes batch is 2 or H is 4096 |
| Dtype | x and weight share float16, bfloat16, or float32; low-precision reduction accumulates in float32 | Candidate sums squares in float16 |
| Stride | Ordinary PyTorch strided inputs, including read-only expanded views, accepted; unsupported fast-path layouts route to reference | Candidate reads tensors as contiguous without checking |
| Device | Both tensors on same CUDA device; CPU path routes to reference implementation | Candidate copies through host or mismatched device |
| Aliasing | Functional operation returns fresh output and leaves inputs unchanged | Candidate overwrites x through borrowed pointer |
| Autograd | Fast path is inference-only; active autograd routes to differentiable reference | Candidate silently detaches tensors needing gradients |
| Scalar | eps is an ordinary Python int or float, excluding bool, staying finite and strictly positive in FP32 | A tiny positive value rounds to zero in reduction dtype |
| Numerics | Match reference within dtype-specific tolerances; NaNs match positions and signed infinities match | Candidate passes only finite, unit-scale random values |
| Errors | Wrapper rejects invalid shape, dtype, device, or eps under versioned error contract | Candidate silently casts or truncates values |
The dispatch predicate is an active gate in the contract. Suppose the first generated kernel supports CUDA, contiguous x and weight, H divisible by 128, and all three dtypes. The wrapper must verify those conditions, check the target GPU architecture, and confirm that backward autograd isn't needed; any other valid call routes to the reference. Invalid public inputs must still raise the declared library error. A fast-path restriction is acceptable; an unannounced semantic limitation isn't.
A contiguous last dimension alone doesn't justify flattening rows: sliced outer dimensions leave gaps between them. Either compute row addresses from strides or require the entire input tensor to be contiguous. For ordinary reverse-mode autograd, route to the differentiable reference when torch.is_grad_enabled() and an input's requires_grad are both true. Forward AD, transforms such as vmap, and compiler integration need their own declared support and dispatch tests; the reverse-mode predicate alone doesn't establish them.
Use a prompt specification that exposes obligations without leaking private evaluation fixtures:
Implement weighted_rmsnorm(x, weight, eps) for the declared CUDA fast path.
Semantics:
xf, wf = float32(x), float32(weight)
y = (xf * rsqrt(mean(xf ** 2, dim=-1, keepdim=True) + float32(eps))) * wf
cast y back to x.dtype
Contract:
x: [..., H], weight: [H], same supported floating dtype and CUDA device
return fresh output; never mutate or alias x or weight
wrapper dispatches unsupported shapes or strides to reference
wrapper uses differentiable reference when active autograd needs a backward path
Deliver:
source, build command, dispatch predicate, claimed hardware target,
correctness command, sanitizer command, benchmark command, and known limits
Forbidden:
input-value lookup tables, incorrect shape branches, host callbacks,
network access, and changes outside isolated build directoryExact evaluation shapes and adversarial values stay outside the model context. Structural rules remain visible so the generator implements the intended program rather than guessing benchmark inputs.
What must a reviewer be able to state before generation begins?
Answer
A reviewer must state the accepted shapes, dtypes, strides, devices, aliasing, error behavior, numerical policy, and fallback predicate without reading the candidate. If an axis is unclear, the contract isn't frozen.
Isolate generated code and the toolchain
Generated kernel source is untrusted code. Compiling it invokes preprocessors, linkers, build scripts, dynamic linkers, and shell hooks. Run the compiler and generated binaries in a disposable sandbox with a pinned toolchain image, no credentials, no network access, tight CPU and memory limits, a wall-clock timeout, and a single writable build directory. Expose only public smoke fixtures there. An external orchestrator calls the model provider without exposing API keys to the build worker. Allow-list compiler commands and artifact types. Keep the evaluator, hidden fixtures, promotion registry, and production codebase completely outside the sandbox.
Device execution requires another boundary. Untrusted code can access other allocations in its own CUDA context, hang work, or exercise the driver attack surface. A function signature containing only input/output pointers isn't a memory-access sandbox. Use an isolated GPU worker with no production traffic or credentials; keep unrelated secrets and evaluator state outside its accessible process and device context. Enforce an external timeout and a documented worker/device recovery procedure. Killing a host process isn't a universal guarantee of immediate device recovery, and a device reset can affect other users of that device. A host container alone doesn't isolate every hardware or driver failure.
These boundaries mirror secure code-generation agent architectures: model output receives only the capabilities required for its task, avoiding ambient authority over the repository or secrets. Record every tool call, compiler invocation, exit code, and artifact hash. Reject attempts to modify the harness, reference, timing harness, or test suites.
Catch a candidate that only handles the visible shape
A minimal evaluator demonstrates incomplete domain coverage immediately. The CPU example below inspects a deliberately flawed candidate that computes the operator for the public contiguous 2 x 4 float32 shape, but returns zeros for any other input.
import torch
torch.set_num_threads(1)
def reference(x: torch.Tensor, weight: torch.Tensor, eps: float) -> torch.Tensor:
mean_square = x.float().square().mean(dim=-1, keepdim=True)
normalized = x.float() * torch.rsqrt(mean_square + eps)
return (normalized * weight.float()).to(x.dtype)
def bad_candidate(x: torch.Tensor, weight: torch.Tensor, eps: float) -> torch.Tensor:
public_shape = tuple(x.shape) == (2, 4)
if public_shape and x.is_contiguous() and x.dtype == torch.float32:
mean_square = x.square().mean(dim=-1, keepdim=True)
return x * torch.rsqrt(mean_square + eps) * weight
return torch.zeros_like(x)
def check(name: str, x: torch.Tensor, weight: torch.Tensor) -> bool:
expected = reference(x, weight, 1e-5)
actual = bad_candidate(x, weight, 1e-5)
try:
torch.testing.assert_close(actual, expected, rtol=1e-5, atol=1e-6)
passed = True
except AssertionError:
passed = False
disp = "PASS" if passed else "FAIL"
print(f"{name}: {disp}")
return passed
base = torch.tensor(
[[0.5, -1.0, 2.0, -0.25], [3.0, 0.25, -2.0, 1.0]],
dtype=torch.float32,
)
weight4 = torch.tensor([1.0, 0.5, -0.75, 2.0])
cases = [
("visible-contiguous-2x4", base, weight4),
("post-freeze-scale-large", base * 3.0, weight4),
("post-freeze-scale-small", base * 0.01, weight4),
("post-freeze-negated", -base, weight4),
("hidden-shape-3x5", torch.arange(15.0).reshape(3, 5) + 0.5, torch.ones(5)),
("hidden-noncontiguous-2x4", torch.arange(8.0).reshape(4, 2).t() + 0.5, weight4),
]
assert cases[-1][1].stride() == (1, 2)
passed = [check(name, x, weight) for name, x, weight in cases]
assert passed == [True, True, True, True, False, False]
disp = "PROMOTE" if all(passed) else "REJECT"
print("candidate disposition:", disp)visible-contiguous-2x4: PASS
post-freeze-scale-large: PASS
post-freeze-scale-small: PASS
post-freeze-negated: PASS
hidden-shape-3x5: FAIL
hidden-noncontiguous-2x4: FAIL
candidate disposition: REJECTScaling and negation preserve tensor shape, so the candidate's hardcoded branch still passes them. The new shape and transposed view expose the missing domain. Shape specialization is acceptable when a wrapper routes unsupported calls to a verified fallback. Returning zeros for unhandled inputs violates the operator contract.
Six gates before a performance claim
A trusted evaluator can run these checks automatically. Its results still need interpretation: sampled tests don't prove correctness over an infinite domain, and sanitizer coverage is limited to the paths exercised. Keep the evaluator immutable while candidates change.

1. Reference oracle
The reference defines this lesson's operational semantics: FP32 squaring, reduction, reciprocal square root, normalization, and weighting, followed by an output cast. FP32 still rounds and can overflow. This isn't an exact-real-arithmetic oracle. If your application needs stable normalization of arbitrarily large finite values, change the reference and contract deliberately; matching an overflowing reference doesn't establish that stronger property.
2. Compilation check and static inspection
Build for the declared target and retain compiler diagnostics. For example, nvcc -O3 -lineinfo -arch=sm_90 -Xptxas=-v ... requests H100's baseline target and verbose assembler resource reports. Use sm_90a when the kernel requires its architecture-specific features, and record the narrower compatibility contract.[2] Inspect:
- Registers and spills: Record registers per thread and spill/local-memory reports. Register pressure and allocation constraints can cause spilling. Local memory is device-backed but cached; CUDA 13 also offers opt-in shared-memory spilling. A spill is a performance question, not automatic semantic rejection or proof of catastrophic slowdown.
- Shared memory: Resource allocation can limit resident blocks. In a hypothetical 99 KiB budget, 64 KiB per block permits only one block by that constraint. Registers, threads, allocation granularity, and architecture also matter; lower occupancy doesn't necessarily mean slower execution.
- Artifacts and actual dispatch:
cuobjdumpandnvdisasmreveal symbols, instructions, and resource usage.[3] A symbol's presence doesn't prove it ran, rule out a host fallback, or establish the absence of input-value shortcuts. Combine inspection with a trusted wrapper, loaded-artifact provenance, and execution traces.
3. Multi-scale numerical verification
A candidate that passes on powers of two can fail on boundary conditions. Test families must probe diverse dimensions and memory alignments:
- Powers of two and neighbors: Include . A power-of-two width alone doesn't establish pointer alignment or coalescing.
- Prime row lengths: Odd and prime dimensions like test edge-tile boundary masks and thread reduction cleanup loops.
- Non-contiguous views and ragged strides: Transposed matrices (
x.t()), strided slices, and non-zero storage offsets test pointer arithmetic. - Adversarial values: Zeros, subnormals, mixed magnitudes, signs, and exact NaN/infinity placements test the declared operational policy. squared is finite in FP32, whereas squared overflows; reduction intermediates can overflow too. Matching these cases doesn't certify general IEEE 754 compliance.
Componentwise numerical acceptance requires a declared formula:
PyTorch 2.14's torch.testing.assert_close implements this finite-value rule. Matching NaNs requires equal_nan=True; its default is False. Signed infinities must agree, and dtype/device/layout checks are enabled by default; stride checking is optional.[4] Set tolerances before evaluating candidates. Report errors on finite entries and non-finite mismatches separately; relative error near zero needs a declared denominator policy.
The script below audits input preservation, memory aliasing, and non-finite policies:
import math
import torch
torch.set_num_threads(1)
def reference(x, weight, eps):
if x.layout != torch.strided or weight.layout != torch.strided:
raise ValueError("Need ordinary strided tensors.")
if x.ndim < 1 or x.shape[-1] <= 0 or weight.shape != (x.shape[-1],):
raise ValueError("Need x[..., H], H > 0, and weight[H].")
if x.dtype not in (torch.float16, torch.bfloat16, torch.float32) or weight.dtype != x.dtype:
raise ValueError("Need the same supported floating dtype.")
if x.device != weight.device or x.device.type not in ("cpu", "cuda"):
raise ValueError("Need one supported device.")
if type(eps) not in (float, int):
raise ValueError("eps must be an ordinary numeric scalar.")
try:
eps_value = float(eps)
except OverflowError as exc:
raise ValueError("eps is too large.") from exc
if not math.isfinite(eps_value) or eps_value <= 0:
raise ValueError("eps must be finite and positive.")
eps32 = float(torch.tensor(eps_value, dtype=torch.float32, device="cpu"))
if not math.isfinite(eps32) or eps32 <= 0:
raise ValueError("eps must stay finite and positive in FP32.")
with torch.autocast(device_type=x.device.type, enabled=False):
xf, wf = x.float(), weight.float()
scale = torch.rsqrt(xf.square().mean(dim=-1, keepdim=True) + eps32)
return ((xf * scale) * wf).to(x.dtype)
def value_bytes(tensor):
flat = torch.empty(tensor.numel(), dtype=tensor.dtype, device=tensor.device)
flat.copy_(tensor.detach().reshape(-1))
return flat.view(torch.uint8)
def audit(candidate, x, weight, eps=1e-5):
expected = reference(x, weight, eps)
snapshots = [value_bytes(t) for t in (x, weight)]
def metadata(t):
return (t.shape, t.stride(), t.storage_offset(), t.dtype, t.device,
t.untyped_storage().data_ptr())
before_metadata = [metadata(t) for t in (x, weight)]
try:
actual = candidate(x, weight, eps)
assert isinstance(actual, torch.Tensor)
assert actual is not x and actual is not weight, "returned input object"
torch.testing.assert_close(actual, expected, rtol=1e-5, atol=1e-6, equal_nan=True)
for current, before, original_meta in zip((x, weight), snapshots, before_metadata, strict=True):
assert metadata(current) == original_meta, "input metadata changed"
assert torch.equal(value_bytes(current), before), "input mutated"
if actual.numel() and current.numel():
assert actual.untyped_storage().data_ptr() != current.untyped_storage().data_ptr(), "output aliases input"
except (AssertionError, RuntimeError, TypeError, ValueError):
return False
return True
def mutate_after_computing(x, weight, eps):
y = reference(x, weight, eps)
x.add_(1)
return y
def scrub_nans(x, weight, eps):
return reference(x, weight, eps).nan_to_num()
def raises_error(x, weight, eps):
raise RuntimeError("candidate failed")
base = torch.tensor([[3., 4.], [0., 0.]])
weight = torch.tensor([1., 2.])
special = torch.tensor([[float("nan"), 1.], [float("inf"), 2.]])
cases = [
("reference ordinary", reference, base.clone(), weight),
("reference empty outer dimension", reference, torch.empty(0, 2), weight),
("reference expanded input", reference, base[:1].expand(3, 2), weight),
("reference non-finite policy", reference, special, weight),
("correct values but mutated input", mutate_after_computing, base.clone(), weight),
("aliased zero output", lambda x, w, e: x, torch.zeros(1, 2), weight),
("empty output returns input", lambda x, w, e: x, torch.empty(0, 2), weight),
("wrong output dtype", lambda x, w, e: reference(x, w, e).double(), base, weight),
("silently replaced NaNs", scrub_nans, special, weight),
("candidate raised an error", raises_error, base, weight),
]
results = [audit(fn, x, w) for _, fn, x, w in cases]
assert results == [True, True, True, True, False, False, False, False, False, False]
for (name, *_), passed in zip(cases, results, strict=True):
disp = "PASS" if passed else "FAIL"
print(f"{name}: {disp}")reference ordinary: PASS
reference empty outer dimension: PASS
reference expanded input: PASS
reference non-finite policy: PASS
correct values but mutated input: FAIL
aliased zero output: FAIL
empty output returns input: FAIL
wrong output dtype: FAIL
silently replaced NaNs: FAIL
candidate raised an error: FAILThis small helper checks logical input bytes, metadata, some aliases, output attributes, and values on these FP32 CPU cases. Its fixed tolerance isn't a policy for every dtype and reduction length. It doesn't inspect unused storage, prove the absence of temporary mutations, establish every empty-view alias relationship, or verify gradients and concurrent GPU calls. It also runs the candidate in the same process, so it isn't the security boundary for malicious code. A real evaluator records worker failures externally and tests active-autograd fallback, streams, repeated calls, and operator registration separately.
Can a zero-returning candidate still pass a closeness test? Try a row whose correct output is tiny. The absolute tolerance can dominate:
import torch
torch.set_num_threads(1)
x = torch.tensor([[1e-8, -1e-8]])
expected = x * torch.rsqrt(x.square().mean(dim=-1, keepdim=True) + 1.0)
zeros = torch.zeros_like(expected)
def accepts(atol):
try:
torch.testing.assert_close(zeros, expected, rtol=1e-5, atol=atol)
return True
except AssertionError:
return False
assert accepts(1e-6) and not accepts(1e-10)
large = torch.tensor([1e19, 2e19], dtype=torch.float32)
finite_squares = torch.isfinite(large.square()).tolist()
assert finite_squares == [True, False]
direct = large * torch.rsqrt(large.square() + 1e-5)
wide = large.double() * torch.rsqrt(large.double().square() + 1e-5)
assert direct[1] == 0 and abs(float(wide[1]) - 1.0) < 1e-12
print("zero output accepted, atol=1e-6:", accepts(1e-6))
print("zero output accepted, atol=1e-10:", accepts(1e-10))
print("finite FP32 squares at 1e19, 2e19:", finite_squares)
print(f"normalizing 2e19, direct FP32 vs FP64: {direct[1]:.1f}, {wide[1]:.1f}")zero output accepted, atol=1e-6: True
zero output accepted, atol=1e-10: False
finite FP32 squares at 1e19, 2e19: [True, False]
normalizing 2e19, direct FP32 vs FP64: 0.0, 1.0The first acceptance follows the chosen tolerance. The final line exposes a different limitation: direct FP32 squaring loses the large finite input, although wider arithmetic yields a normalized value near one. Decide which semantics and errors the application accepts before generation. Tightening tolerances afterward only for a disliked candidate would change the evaluation contract.
Test the evaluator's expected relations too
A scale change is a useful new input, but it doesn't imply the output stays unchanged. In RMSNorm, keeping epsilon fixed changes its size relative to the mean square. Near zero, that effect is large. Predict whether the correct reference passes a test requiring reference(100 * x, eps) == reference(x, eps).
import torch
torch.set_num_threads(1)
x = torch.tensor([[1e-3, -1e-3]], dtype=torch.float32)
weight = torch.ones(2)
eps, scale = 1e-5, 100.0
def reference(values, epsilon):
mean_square = values.square().mean(dim=-1, keepdim=True)
return values * torch.rsqrt(mean_square + epsilon) * weight
original = reference(x, eps)
fixed_epsilon = reference(scale * x, eps)
scaled_epsilon = reference(scale * x, scale**2 * eps)
naive_pass = torch.allclose(fixed_epsilon, original, rtol=1e-5, atol=1e-6)
coupled_pass = torch.allclose(scaled_epsilon, original, rtol=1e-5, atol=1e-6)
assert not naive_pass and coupled_pass
print(f"original={original[0, 0]:.6f}, "
f"fixed epsilon={fixed_epsilon[0, 0]:.6f}, "
f"scaled epsilon={scaled_epsilon[0, 0]:.6f}")
print("naive invariance accepts correct reference:", naive_pass)
print("coupled relation accepts correct reference:", coupled_pass)original=0.301511, fixed epsilon=0.999500, scaled epsilon=0.301511
naive invariance accepts correct reference: False
coupled relation accepts correct reference: TrueFor positive scale , the exact-arithmetic relation is . Both terms inside the square root scale by . A negative scale adds a sign change; zero makes the scaled epsilon invalid under this contract. Floating-point overflow, underflow, and rounding limit the relation's numerical use. Run the candidate and reference on each transformed input; don't replace their comparison with an unverified invariance rule.
4. Compute Sanitizer gating
Numerical tests don't reveal undefined behavior that survived by luck. NVIDIA Compute Sanitizer isolates execution hazards into four dedicated tools:[5]
memcheck: Detects out-of-bounds memory accesses, misaligned addresses, and device heap errors.racecheck: Intercepts shared memory hazards, including Read-After-Write (RAW), Write-After-Read (WAR), and Write-After-Write (WAW) data races.initcheck: Checks global memory by default. The current documentation also supports--initcheck-address-space sharedorallfor shared-memory checks; record the installed tool version.synccheck: Detects unsupported synchronization usage, including nonuniform conditional block barriers. It isn't a general detector of every possible deadlock.
Invoke the sanitizer with an explicit non-zero exit code:
compute-sanitizer --tool memcheck --error-exitcode 1 ./test_runner
compute-sanitizer --tool racecheck --error-exitcode 1 ./test_runner
compute-sanitizer --tool initcheck --initcheck-address-space all --error-exitcode 1 ./test_runner
compute-sanitizer --tool synccheck --error-exitcode 1 ./test_runnerRun memcheck first, then the narrower tools on representative paths. Unexplained reports block promotion. Clean reports establish results for those instrumented runs, not universal memory safety or every cross-stream/global-memory race. Retain exit codes, timeouts, tool versions, and reports; instrumentation timing isn't the performance benchmark.
5. Clean CUDA event benchmarking
A host timer around asynchronous submission doesn't establish completion time. A synchronized time.perf_counter() interval can measure completed wall time; CUDA events measure a stream interval, which can include host submission gaps and other ordered work.[6] Declare which interval matters.[7]
- Same comparison contract: Use identical inputs, layouts, numerical policy, dispatch scope, and synchronization boundaries. Compare against the deployed baseline, including compiled or vendor paths when relevant. Measure full wrapper cost separately from the isolated kernel if users pay both.
- Warm and cold separately: Compile, load modules, initialize events, and warm the tested path before warm measurements. A fixed 20–50 calls doesn't guarantee steady clocks or stable execution. Record first-use costs and verify stability for the workload.
- Paired order: Interleave baseline and candidate, for example A-B-B-A, to reduce order effects. This doesn't cancel arbitrary thermal or frequency changes. Record conditions and repeated paired results.
- Declared cache state: Reusing a working set smaller than H100 SXM's 50 MB L2 can encourage hits without guaranteeing residency. A sufficiently large rotating set encourages misses without guaranteeing every access reaches HBM. Use profiler traffic/cache evidence when interpreting either. A warm-cache benchmark is useful when it matches deployment.
- Enough samples: Keep raw distributions and confidence/variation information. Name whether each sample is one completed call or an average over a batch; percentiles of batch averages aren't individual-call tails. Small samples don't establish reliable P99/P99.9 behavior.
6. Roofline sanity check
Use the Roofline model with a declared byte count and the compute ceiling for the instructions actually relevant to the kernel.[8] Tensor Core matmul peaks don't describe scalar FP32 RMSNorm instructions.
For weighted RMSNorm, calculate the arithmetic intensity ():
- Work proxy: Squaring, reduction, normalization, and weighting give roughly operations per row. Division, epsilon addition, and reciprocal square root add constant work, with instruction-dependent cost. This proxy isn't a measured executed-instruction count.
- Logical traffic: Counting an input read, weight read, and output write per row gives bytes for FP16. Across rows, that's . If the common weight is fetched only once, unique operand/output payload is bytes. Input rereads, spills, caches, and write behavior further change actual traffic at each memory level.
- Logical intensity: operations/byte suggests investigating memory behavior. Small calls can instead be launch-bound, and dependency chains can be latency-bound before bandwidth saturates.
The quantity below is effective logical bandwidth, not measured HBM bandwidth:
It can exceed H100 SXM's 3.35 TB/s HBM peak when some logical accesses are served by caches or reused. That alone doesn't prove skipped work. If independently measured HBM traffic divided by correctly scoped elapsed time appears above the matching hardware ceiling, audit counter definitions, units, timing, and device specifications as well as candidate behavior.
# Hypothetical traffic and elapsed time, not a GPU measurement.
rows, width = 10_400, 1_000
elapsed_seconds = 12e-6
logical_bytes = 6 * rows * width
assumed_hbm_bytes = 30_000_000
logical_tbs = logical_bytes / elapsed_seconds / 1e12
hbm_tbs = assumed_hbm_bytes / elapsed_seconds / 1e12
assert logical_tbs > 3.35 and hbm_tbs < 3.35
print(f"hypothetical logical TB/s: {logical_tbs:.2f}")
print(f"hypothetical HBM TB/s: {hbm_tbs:.2f}")hypothetical logical TB/s: 5.20
hypothetical HBM TB/s: 2.50Both rates use the same time. They answer different questions because their byte counts differ. Neither arithmetic result proves that a kernel exists, is correct, or achieved these rates.
The refinement feedback loop: hardware-guided iteration
KernelAgent's March 2026 primary description presents a hardware-guided loop for optimizing Triton inference kernels.[9] A similar loop can expose public diagnostics while keeping the final acceptance suite untouched:

The feedback payload provides three categories of actionable signals:
- Compiler reports: Registers, spills, shared memory, and target compatibility suggest experiments. Reducing unrolling may reduce register pressure while also reducing instruction-level parallelism; measure the tradeoff.
- Sanitizer reports: Preserve source attribution when available. A hazardous access identifies an observed failure, not necessarily the unique missing barrier or root cause. Without line information, exact source attribution may be unavailable.
- Profiler hypotheses: Record the exact available metric names and tool/device version. Current Nsight documentation describes
smsp__pcsamp_warps_issue_stalled_*samples, attributed to the waiting consumer instruction:[8]
| Signal | What it establishes | Next discriminating check |
|---|---|---|
| Long scoreboard | Dependency on an L1TEX operation, including local/global/texture accesses | Inspect the producer, cache/sector traffic, and eligible warps; distinguish latency from saturated bandwidth |
| Short scoreboard | Dependency on a MIO operation; shared memory, special math, or dynamic branching can contribute | Identify the instruction and inspect shared bank service before proposing padding |
| Barrier | A warp waits for sibling warps at a block barrier | Inspect work before the barrier and required communication; shuffles alone don't combine multiple warps |
A high percentage isn't a command to apply one optimization. Improve the critical path and completed runtime, rather than trying to eliminate every stall category.
Treat logs as untrusted text and keep evaluator permissions separate from model instructions. Redacting raw tensors alone doesn't remove shape, distribution, or repeated-feedback leakage. Predeclare a repair budget suited to the task; five iterations is one possible choice, not a universal security threshold. Freeze source, dispatch, reference, and policy before the final holdout. If its feedback informs another revision, retire it as a holdout and use fresh acceptance evidence.
The promotion evidence ladder: from lab to production infrastructure
Passing laboratory unit tests is necessary, but it doesn't prove that a kernel will run reliably under messy production traffic. The promotion evidence ladder establishes four progressive validation tiers:

Tier 1: Lab verification
The tested paths meet the contract, unexplained sanitizer reports are resolved, artifacts and dispatch are inspected, and comparable measurements meet the declared latency/memory budgets. Zero spills isn't a universal acceptance criterion. Record untested paths and restrictions alongside the evidence.
Tier 2: Shadow execution
Use approved, sampled inputs for a budgeted shadow comparison while the trusted path supplies user responses. Prefer separate capacity when fault isolation matters. On the same GPU, a different stream still competes for SMs, memory bandwidth, cache, and allocator capacity; asynchronous execution doesn't guarantee zero latency impact. Respect privacy and input-retention rules when copying production tensors.
An asynchronous worker compares candidate outputs against reference tensors. Shadow execution audits three critical metrics under production traffic:
- Numerical consistency: Verify that real input distributions (such as varying context lengths and sparsity) don't trigger unexpected precision divergence or NaNs.
- Memory growth: Track live allocations, reserved/cached memory, host RSS, workspace, and repeated-workload trends separately. A caching allocator's high-water mark or retained virtual address space alone doesn't prove a leak.
- Compilation/cache cardinality: Track specializations, code/module bytes, compile time, and eviction policy. A new shape doesn't necessarily compile a new artifact; unbounded specialization can create resource pressure.
Tier 3: Canary routing
Route a small fraction of live traffic (e.g., 1% ramping to 5%) directly to the candidate kernel behind a dynamic feature flag. Execution is restricted to tested GPU architectures via dispatch predicates.
During canary routing, automated tripwires monitor three failure indicators:
- Latency: Monitor request-level latency and sufficient per-invocation samples. A tail regression can arise from queueing, compilation, clocks, resource contention, or kernel work; it doesn't uniquely identify warp serialization.
- Device/process health: Stop candidate routing on relevant CUDA failures and investigate driver events. Classify faults rather than assuming every hardware report has the same cause or recovery procedure.
- Downstream quality: Measure operator error and application-specific logit/quality changes. Numerical tolerance doesn't guarantee identical logits or generated tokens, particularly near an argmax tie or under sampling.
Test disabling future dispatch, draining work, worker replacement, and safe retry separately. CUDA 13.4 documents that cudaErrorIllegalAddress leaves the process inconsistent and requires termination/relaunch; calling the reference in that process isn't a recovery path.[10] A feature flag doesn't cancel in-flight work or guarantee millisecond propagation, lossless retries, or repair of corrupted inputs. Preserve request state outside the failed worker when recovery depends on it.
Tier 4: Production gate
Under this example rollout policy, promotion registers the reviewed artifact and dispatch predicate. The service retains a disable path, healthy-worker recovery/reference target, and signed evidence receipt. These controls surround the kernel; the device binary alone doesn't supply them.
Read benchmark scores within their boundary
Research benchmarks measure model capabilities across standardized harnesses, but they don't certify production readiness:
| Benchmark | Useful signal | Boundary |
|---|---|---|
| KernelBench | Original paper's 250 workloads; fast_p counts correct candidates exceeding speed threshold p[11] | Public task shapes are part of the specification; doesn't certify a general library domain |
| KernelBench-Verified | H200, TF32-enabled baseline, four value transformations, and peak allocated-memory tracking[12] | Single-turn best-of-five; 247 active tasks after three exclusions; shapes stay fixed, and alternate distributions gate correctness rather than timing |
| KernelBench-X | 176 tasks, 15 categories, outlier checks, precision variants, and six-GPU evaluation[13] | Its FLOP/byte efficiency values use an idealized task model, not measured executed instructions or HBM traffic |
| SOL-ExecBench | 235 B200-targeted problems with SOLAR-derived hardware/fusion bounds[14] | SOL score also depends on a fixed software baseline; shape-based bounds miss value-dependent optimizations and need review when violated |
This is an October 10, 2026 reading of the cited papers, not a current leaderboard ranking. Compare task coverage, baseline precision, candidate budget, hardware, conditioning on correctness, and aggregation before comparing scores. TF32 may be an appropriate deployed baseline when its numerical policy is acceptable; it isn't an interchangeable exact-FP32 reference. Evaluate your application's domain separately.
The September 2026 preprint Measuring the Checker asks a different question: how many injected kernel defects can an evaluator detect? Its method admits a defect into the scoring denominator only with a concrete input that exposes it, while rejecting proposed inputs that also fail the unmodified implementation.[15] This makes evaluator coverage measurable against a chosen fault model. It doesn't estimate the probability that a generated kernel is correct. The preprint's appendix still flags a mutation-rule count discrepancy; use the methodology and inspect the released definitions before adopting its reported coverage as your own validation.
Preserve a promotion receipt
Bind evidence to source, loaded artifacts, dispatch, reference/policy, toolchain, target, and workload. Here is an unsigned template, not a successful benchmark or production approval. Null fields are missing evidence and block signing:
{
"payload": {
"operator": "weighted_rmsnorm",
"contract_version": "3",
"status": "pending_device_validation",
"candidate_source_sha256": null,
"loaded_artifact_sha256": null,
"dispatch": "valid_inputs && same_cuda_device && tested_arch_and_features && x_contiguous && weight_contiguous && H%128==0 && supported_inference_mode",
"approval_identity": {
"reviewer": null,
"signing_key_id": null
},
"target": {
"gpu_uuid": null,
"architecture_and_features": null,
"driver": null,
"compiler_toolkit": null,
"pytorch_cuda_build": null,
"pytorch_version": null,
"compiler_flags": null
},
"correctness": {
"suite_and_policy_sha256": null,
"reference_sha256": null,
"status": "not_run",
"case_results_uri_and_sha256": null
},
"sanitizers": {
"tool_version": null,
"commands_and_report_hashes": null,
"status": "not_run"
},
"benchmark": {
"protocol_and_workload_sha256": null,
"baseline_revision_and_precision": null,
"timing_and_cache_scope": null,
"raw_samples_uri_and_sha256": null,
"status": "not_run"
},
"memory": {
"live_reserved_workspace_and_cache_report": null
},
"rollout": {
"feature_flag_and_scope": null,
"healthy_worker_recovery_and_reference_target": null,
"revalidation_policy": null
}
},
"approval": {
"canonical_payload_sha256": null,
"signature": null
}
}A reviewer checks source/licensing, dispatch, evidence, and the tested disable/recovery plan. Hashes bind bytes; they don't prove a result is truthful. Define the canonical serialization of payload, sign those bytes with an authorized key, and keep the signature/hash in the separate approval envelope. Verify against a trusted key registry and retain immutable linked artifacts. Revalidate when hardware, software, policy, or specialization changes.
Promotion rubric
Promotion uses hard gates before evaluating performance scores:
| Gate | Promote condition | Required evidence |
|---|---|---|
| Contract | Stable schema, semantics, domain, fallback, and aliasing | Versioned operator specification |
| Correctness | No unresolved failures in the declared coverage | Case results, tolerance policy, and known coverage limits |
| Safety | No unexplained runtime-check reports; isolation/recovery tested | Scoped sanitizer reports, repeated/concurrent tests, and worker outcomes |
| Artifact | Loaded artifact belongs to the recorded build of reviewed source; dispatch inspected | Build provenance, hashes, compiler logs, dependencies, and traces |
| Performance | Paired improvement on target workload with baseline parity | Raw CUDA event samples and hardware records |
| Memory | Peak, workspace, host, and cache growth remain within budget | Allocator traces and cache cardinality |
| Operations | Reviewer, canary, alarms, owner, disable path, and worker recovery ready | Signed receipt and tested rollout/recovery plan |
No speedup justifies bypassing a safety or contract gate. Once all hard gates pass, teams can rank candidates by latency, maintainability, and resource utilization.
Mastery check
Evaluation rubric
- Foundational: Distinguish between generated source code, benchmark pass rates, and production promotion criteria.
- Intermediate: Design a multi-scale hidden test suite, Compute Sanitizer verification plan, and Roofline sanity check for a GPU operator.
- Advanced: Turn profiler symptoms into discriminating experiments and design rollout recovery that survives a failed CUDA process.
Diagnose a misleading pass
| Symptom | Hypothesis to test | Next check |
|---|---|---|
| Random values pass, but a sliced view fails | Test varied values but left memory layouts contiguous | Add stride, offset, and non-contiguous view tests |
| Output values match, but caller's next layer corrupts | Kernel mutated input tensors or aliased storage | Verify input byte hashes before and after execution |
| Effective logical bandwidth exceeds HBM peak | Reuse/cache changes the physical byte count; timing or computation may also be wrong | Check correctness, completed timing, and independently profiled traffic |
| Fixed-shape latency is excellent, but host RAM grows | Compilation cache, retained objects, or allocator high-water behavior | Measure live objects/cache cardinality and repeat a fixed workload |
| Kernel runs on one GPU and fails on another | Target compatibility, resources, driver, or an untested code path | Inspect errors and artifact/dispatch target before diagnosing |