Skip to content

BLAS beta==0 write-only semantics (fixes 0*NaN=NaN poisoning from cold scratch)#19

Merged
plancherb1 merged 7 commits into
mainfrom
beta0-write-only
Jul 8, 2026
Merged

BLAS beta==0 write-only semantics (fixes 0*NaN=NaN poisoning from cold scratch)#19
plancherb1 merged 7 commits into
mainfrom
beta0-write-only

Conversation

@plancherb1

Copy link
Copy Markdown
Member

What

Every beta-taking op used to blend with a raw alpha*res + beta*dst, reading the destination even when beta == 0 — so cold/uninitialized scratch holding NaN poisoned the result (0*NaN = NaN). This violates BLAS/cuBLAS convention (beta==0 ⇒ output is write-only) and was documented as a caller-side footgun (old debugging-guide §1c). It is the root cause of the GRiD/Parallel-DDP "heavy plant give-up" NaN: GRiD's generated RNEA writes s_vaf v/a bodies via gemv_segmented(..., beta=0) over cold smem, and diverged line-search rollouts leave NaN on the SM.

All blends now route through beta_blend(acc, beta, dst) (src/base/barrier.cuh): dst is read only when beta != 0, and the hot arm is an explicit __fmaf_rn/__fma_rn — a plain expression left the FMA-contraction choice to ptxas per kernel and broke warp-vs-block bit-identity (caught by test_syrk_warp; the explicit intrinsic restored it, 72/72).

Covered: gemv / gemv_strided / gemv_segmented / gemv_reduced, gemm (untiled + tile4 + tiled) / gemm_strided / gemm_reduced, syrk / syrk_reduced, symm, congruence, axpby — block, warp::, and cgrps:: surfaces (shared impls). Docstrings + debugging-guide §1c updated to the new guarantee.

Bit-compat: identical results at beta ∈ {0, 1} (every known consumer's usage — GRiD/PDDP gates verified bit-identical downstream); last-ULP may shift at fractional beta, where the old contraction was unspecified anyway.

Tests

  • New regression tests: beta overloads called with beta=0 into NaN-poisoned destinations must come back clean — test_l2.py::test_gemv{,_strided,_segmented}_beta0_poisoned_y_no_read, test_l3.py::test_gemm_rt_betaform_beta0_no_read (incl. a tile4-path shape).
  • Ran locally (sm_120): test_l2 + test_l3 512 passed; test_syrk 610 passed after the FMA fix (72/72 warp bit-identity).

Handoff TODO (before merge to main)

  • Full suite pass (pytest test/)
  • Fresh signed gpu-proof receipt (./test/run_gpu_proof.sh) bundled per the main-branch gate
  • After merge: bump GLASS pins + regen in consumers — GRiD (GCG vendors these files into every grid.cuh), GATO, HJCD-IK, MPCGPU

Downstream validation already done (Parallel-DDP)

Vendored this diff + regenerated iiwa14/iiwa14_heavy: qNaN-poisoning the whole vaf arena is now a no-op (was 6272 nonfinite → solve death), all BP inputs finite over full solves, iiwa gates bit-identical (51170.97265625 iLQR / 55011.00390625 DDP), and the heavy-swing repro goes from rho give-up at iter 57 (cost 4267.9) to a converged swing (cost 1725.1, pos_err 0.38 rad).

🤖 Generated with Claude Code

@plancherb1 plancherb1 merged commit 08b98a7 into main Jul 8, 2026
3 checks passed
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.

1 participant