Skip to content

perf(rmsnorm): drop redundant out_sum reload in fused add_rmsnorm2_q8 (bit-identical)#191

Closed
devmixa702 wants to merge 1 commit into
gittensor-ai-lab:mainfrom
devmixa702:perf/rmsnorm-q8-drop-redundant-reload
Closed

perf(rmsnorm): drop redundant out_sum reload in fused add_rmsnorm2_q8 (bit-identical)#191
devmixa702 wants to merge 1 commit into
gittensor-ai-lab:mainfrom
devmixa702:perf/rmsnorm-q8-drop-redundant-reload

Conversation

@devmixa702

Copy link
Copy Markdown

Summary

Removes a redundant global-memory read-back in add_rmsnorm2_q8_kernel (kernels/csrc/cuda/fused/rmsnorm.cu) on the bs=1 decode hot path. Bit-identical output — this is a pure bytes-per-token reduction, not a numerics change.

Where it runs

add_rmsnorm2_q8_kernel executes twice per transformer layer (post-attention + post-MoE fused norm→quant, the default path) as a single 256-thread block. At decode (bs=1) a one-block kernel is gated by global-memory latency, so shaving a full-width load off it matters per token, every layer.

The redundancy

Phase 1 computes the fp32 residual sum sv[8], bf16-rounds it, and stores it to out_sum:

osum4[t] = rn_pack8(sv);                 // rn_pack8 stores __float2bfloat16(sv[j])

Phase 2 then re-reads that exact pack back from global just to obtain the bf16-rounded value:

const uint4* osum4r = reinterpret_cast<const uint4*>(out_sum);
float svb[8], wv[8]; rn_unpack8(__ldg(osum4r + t), svb); ...   // svb[j] = __bfloat162float(stored)

The kernel is launched with exactly one 8-wide pack per thread (blockDim*8 == cols), so thread t is the sole writer and reader of osum4[t], and sv[] is still live in registers. The reloaded value is therefore precisely __bfloat162float(__float2bfloat16(sv[j])) — recomputable from the live registers with the identical two intrinsics.

The change

const uint4* w4 = reinterpret_cast<const uint4*>(weight);
float svb[8], wv[8]; rn_unpack8(__ldg(w4 + t), wv);
#pragma unroll
for (int j = 0; j < 8; j++) svb[j] = __bfloat162float(__float2bfloat16(sv[j]));
  • out_sum is still written (phase 1 unchanged) because downstream graph nodes consume it — only the read-back is dropped.
  • Removes one hidden-width (~4 KB for hidden=2048) global load per invocation × 2 per layer × all layers.

Correctness

Bit-exact by construction: rn_pack8/rn_unpack8 are exactly __float2bfloat16/__bfloat162float, so the register recompute reproduces the reloaded bytes identically. out_norm and the derived Q8_1 block are unchanged bit-for-bit. Expected eval-gate result: ~100% top-1, KL = 0 vs the prior build.

Note

I don't have Blackwell hardware to benchmark locally, so I'm relying on the same-box eval bot for the verified speedup number. The change is argued as correctness-neutral and latency-reducing; if the measured decode delta doesn't clear the significance gate it should score none rather than regress. Happy to iterate on reviewer feedback.

🤖 Generated with Claude Code

add_rmsnorm2_q8_kernel is on the bs=1 decode hot path — it runs twice per
transformer layer (post-attention + post-MoE) as a single 256-thread block,
so it is global-memory-latency bound.

Phase 1 computes the fp32 residual sum sv[8], bf16-rounds it, and stores it
to out_sum (osum4[t] = rn_pack8(sv)). Phase 2 then re-read that same pack
back from global (__ldg(osum4r + t)) purely to get the bf16-rounded value.

Because the kernel is launched with one 8-wide pack per thread
(blockDim*8 == cols), thread t is the SOLE writer and reader of osum4[t],
and sv[] is still live in registers. So the reloaded value is exactly
__bfloat162float(__float2bfloat16(sv[j])) — which we can recompute from the
live registers with the identical two intrinsics. out_norm and the derived
Q8_1 are therefore BIT-IDENTICAL; only a redundant hidden-width (~4 KB)
global load per invocation is removed. out_sum is still written (line 195)
since downstream nodes consume it — only the read-back is dropped.

Correctness: bit-exact by construction (same intrinsics, same input); the
same-box eval gate should show ~100% top-1 and KL = 0 vs the prior build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ai-hpc ai-hpc added the copycat Re-submits an earlier PR's diff — not evaluated; 2 strikes = block label Jul 3, 2026
@ai-hpc

ai-hpc commented Jul 3, 2026

Copy link
Copy Markdown
Member

🐈 Flagged: copycat

This PR re-submits substantially the same diff as the earlier #188. Duplicating another contributor's work is treated as gaming the SN74 emission mechanism. The account has been blocked and this PR closed — zero tolerance, no warning. See .github/COPYCATS.md.

@ai-hpc ai-hpc added the flagged:gaming Eval-gaming / sybil — blocked, not evaluated or merged label Jul 3, 2026
@ai-hpc

ai-hpc commented Jul 3, 2026

Copy link
Copy Markdown
Member

🚩 Flagged: eval-gaming

This PR involves an account blocked for gaming the SN74 emission mechanism (sybil / coordinated duplicate farming): devmixa702.

Per the project's no-gaming policy these accounts are blocked: the PR is not evaluated, scored, or merged. See .github/FLAGGED.md for the evidence and record.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

copycat Re-submits an earlier PR's diff — not evaluated; 2 strikes = block flagged:gaming Eval-gaming / sybil — blocked, not evaluated or merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants