Branch:
race-fix-paddleRepository: A-nnonymous/supersonic-moe (remote:
myrepo)Frontier status: GREEN for the documented FP8 frontier path. Precision, determinism, stress, and integration contracts are understood; no known blocker in the default path.
Authoritative handoff: this root
HANDOFF.md. Historical notes inreports/fp8_upgrade/engineering_log.mdare useful chronology only and contain superseded intermediate claims.
SonicMoE is a SM100/Hopper Mixture-of-Experts expert-MLP engine. The active production path on SM100 uses DeepEP topk metadata, route-level padding, blockscaled FP8 E4M3 + UE8M0 scales, CuTe/CUTLASS/QuACK GEMMs, zero-materialization A_idx gather, fused gated up-projection (GEMM + SwiGLU + z FP8 epilogue quant), FP8 down-projection, FP8-C-load GemmDGated backward, iso32 dz dual quant, and TMA reduce-add wgrad directly into ERNIE/Paddle main_grad. The Paddle integration entrypoint is sonicmoe.ernie_compat.SonicMoEMlpNode; node.step() must run before optimizer.step() to flush native CUTLASS wgrad layout into ERNIE layout.
Hardware/method: Target GPU SM100 (SM100, 148 SMs, HBM3e), nsys GPU-projection over BENCH NVTX range, USE_QUACK_GEMM=1, SONIC_MOE_FP8_MODE=perf, SONIC_MOE_FP8_WGRAD=1.
| Shape | FP8 busy | MFU vs 4500 TFLOPS | TFLOPS | Source |
|---|---|---|---|---|
T=1024,H=3072,I=1536,E=8,K=8 |
566.0 µs | 27.32% | 1229 | reports/fresh_benchmark_ws1/ |
T=2048,H=3072,I=1536,E=8,K=8 |
870.1 µs | 35.54% | 1599 | same |
T=4096,H=3072,I=1536,E=8,K=8 |
1459.1 µs | 42.39% | 1907 | same |
T=8192,H=3072,I=1536,E=8,K=8 |
2659.8 µs | 46.51% | 2093 | same; canonical reference shape |
T=16384,H=3072,I=1536,E=8,K=8 |
5224.9 µs | 47.35% | 2131 | same |
T=8192,H=3072,I=1536,E=16,K=8 |
2800.7 µs | 44.17% | 1987 | same |
T=8192,H=3072,I=1536,E=32,K=8 |
3187.5 µs | 38.81% | 1746 | same |
T=8192,H=4096,I=4096,E=8,K=8 |
8521.7 µs | 51.61% | 2322 | measured MFU peak |
Baseline caveat:
- FP8 vs true BF16 (nsys GPU-projection, same codebase): reference shape
2660 µs(FP8) vs4346 µs(BF16, CuTe DSL GemmGatedSm100/GemmDGatedSm100, zero FP8 kernels) = 1.63x speedup. - BF16 vs S53 official PyTorch: Current BF16 is
4346 µsvs S53's3644 µs(ratio 1.19x). The 19% gap comes from Paddle proxy dispatch overhead (~10%) plus FP8-infrastructure-loaded branch cost (~9%, documented in S53 engineering log Lesson 50). This is expected and NOT a regression. - True BF16 mode: Set
SONIC_MOE_FP8_MODE=""before import.SonicMoEMlpNodenow respects this setting and dispatches to CuTe DSL BF16 GEMMs (GemmGatedSm100 + GemmDGatedSm100). No quantization kernels, no FP8 C-load. - Small batches: FP8 crossover is around
T=3000-4000.
Latest nsys GPU-projection per iter:
1185 µs 44.1% QuACK wgrad GEMMs (4 calls)
441 µs 16.4% GemmGatedSm100ZeroMatBlockscaledQuant (fwd up + SwiGLU + z quant)
400 µs 14.9% GemmDGatedFP8CLoadSm100ZeroMat (bwd actgrad + dSwiGLU)
242 µs 9.0% _colwise_quantize_and_pack (num_warps=1)
148 µs 5.5% token_gather_sum_kernel / token reduce-combine
103 µs 3.8% _dual_varlen_iso32_quantize (num_warps=1)
83 µs 3.1% _quantize_and_pack
37 µs 1.4% other broadcast/index kernels
─────────────────
~2639-2660 µs total
NCU headline for the 6 GEMMs (reference-shape report):
| Role | Tensor pipe | DRAM | L2 hit | regs/thread | Readout |
|---|---|---|---|---|---|
fwd1 GemmGated(fp8 swiglu epi) |
64% | 10.5% | 88.8% | 168 | epilogue/register bound |
fwd2 GemmDefault |
70% | 19.5% | 77.6% | 54 | mostly healthy |
dgrad1 GemmDGated(fp8 C-load) |
42% | 22.5% | 61.3% | 168 | worst per-FLOP kernel; C-load/epilogue/register cliff |
dgrad2 GemmDefault |
81% | 25.9% | 68.6% | 56 | near optimum |
wgrad1 GemmDefault |
84% | 24.8% | 74.0% | 56 | near peak |
wgrad2 GemmDefault + main_grad add |
81% | 15.7% | 78.7% | 54 | TMA add is essentially free |
The current FP8 perf path is speed-oriented and keeps FP8 weight caches hot.
| Item | Current understanding |
|---|---|
| FP8 weight caches | Multiple physical layouts are cached for fwd/down/dgated/actgrad/wgrad. Cache keys include data_ptr + inplace_version + shape/stride; optimizer in-place updates naturally invalidate. |
z activation |
Saved as z_fp8 + UE8M0 scales, avoiding a persistent z_bf16(TK,2I) activation. The reference-shape z_bf16 would be ~384 MiB; FP8 data is ~192 MiB plus scales. |
y1 |
y1_fp8 + scales is passed across _UpProjection → _DownProjection via _PREQUANTIZED_SCALES["fwd"]; the BF16 logical object exists for autograd/flow but hot path consumes FP8. |
| backward peak | Dominated by dz, y1s, colwise/rowwise quant products, dx_expanded, and wgrad accumulators. Previous report gives ~1.3 GiB/layer activation peak at reference shape; exact peak depends on cache retention and stagewise-memory mode. |
SONIC_MOE_STAGEWISE_MEMORY=1 |
Memory-saving mode; expect ~1.0-1.5 GiB peak savings at reference-like shape with ~3-5% cost. |
SONIC_MOE_FP8_RECOMPUTE_Z=1 |
Saves roughly one active z_fp8 lifetime but costs extra up-proj recompute. Default remains off for perf. |
Memory claims older than Session 65 are often apples-to-oranges: some measured no FP8 wgrad, some included per-iter node.step() flush, some used old QuACK overhead. Treat them as historical unless reproduced with bench_mlpnode_topk_nsys.py / current benches.
Precision vs BF16 gold:
| Tensor | Current result |
|---|---|
| output | cos ~0.9979 |
| dx | cos ~0.9975 |
| ds/router score grad | cos ~0.9971-0.9973 |
| dw1 | cos ~0.9975 |
| dw2 | cos ~0.9971-0.9972 |
| RRMSE | < 7.6% in covered precision suite |
Determinism:
tests/fp8_frontier_determinism_test.pyis a hard gate and proves bit-exact repeated fwd/bwd on the frontier path.- This matters because TMA reduce-add, async TMA, and global FP8 caches could otherwise introduce bit-level drift.
Numerical caveats:
- iso32 dz dual quant is validated on real reference-like
dzcaptures with downstream GEMM RRMSE ratio1.000x, but it is not a mathematical identity. Monitorlog2(block_amax / row_amax)and zero ratio if using new distributions; fallback to 1x32 dual quant for high-risk distributions. - TMA reduce-add is not a precision improvement. It keeps fp32
main_gradbut changes the accumulation implementation from epilogue C-load to TMA store-side ADD. It does not do Kahan/pairwise accumulation. - FP8 small values near zero can show large relative error; judge by cosine/RRMSE/absolute error and training loss, not max relative error alone.
for step in range(num_steps):
for mb in microbatches:
out = node(dispatched_hidden_states, tokens_per_expert,
dispatched_indices, dispatched_probs)
out.backward(grad)
node.step() # must run BEFORE optimizer.step()
optimizer.step()
optimizer.clear_grad()node.step() flushes native CUTLASS accumulators:
w1 native [E,2I,H] -> ERNIE [E,H,2I]
w2 native [E,H,I] -> ERNIE [E,I,H]
- Production uses
deepep_topk_to_sonic_metadata(). dispatched_indicesis[N_recv, topk] int32; each row must contain distinct local expert ids where valid.-1means masked.- Route-level padding pads each expert segment to 128 rows. Padding rows gather
x[0]but use score 0, so they contribute no output or gradient. x_gather_idxlives in expert-sorted TK space and points into original token rows. This is the zero-materialization key.
- Always
source .runenv.shon this host; it fixes Python/quack/ptxas/Paddle compat environment. - CuTe compile keys must contain only static model dimensions (
H/I/E/dtype/tile) and neverTK,total_M, capacity, or ISA-packed scale sizes. - Runtime fast-path caches may include exact shape but must have high-watermark eviction.
- Multi-process GPFS JIT is protected by
FileLockand direct.soimport fallback; heterogeneous cold starts are covered bytests/ops/test_jit_concurrent_heterogeneous.py.
Read these before changing the frontier:
| Priority | Source | Why it matters |
|---|---|---|
| 1 | reports/sonic_moe_fp8_frontier_newcomer_guide.md |
Standalone training guide: basics, dataflow, symbols, roofline, precision, expert Q&A |
| 2 | reports/fresh_benchmark_ws1/README.md + sweep.json + mfu_model.json |
Latest 22-point performance sweep and fitted MFU model |
| 3 | reference-shape NCU report | NCU full report and bottleneck reasoning for 6 GEMMs |
| 4 | docs/gemm_dgated_fp8cload.md |
GemmDGatedFP8CLoad deep-dive: Int16 trick, dSwiGLU 9-SASS decomposition, FP8 blockscaled dequant, zero-materialization gather |
| 5 | docs/expert_interleave_weight_layout.md |
Expert Interleave 权重布局设计动机 — 5 层全栈收益分析 + 独创性论证 |
| 6 | reports/sonic_moe_comprehensive_analysis.md |
Broad analysis report and newcomer summary |
| 7 | sonicmoe/functional/__init__.py |
_UpProjection / _DownProjection orchestration and FP8 state transfer |
| 8 | sonicmoe/quack_utils/gemm_sm100_fp8_zeromat.py |
zero-materialization SM100 specialization |
| 9 | sonicmoe/quack_utils/gemm_gated.py |
fused gated GEMM + epilogue blockscaled quant |
| 10 | sonicmoe/quack_utils/gemm_dgated.py |
FP8 C-load dGated backward |
| 11 | sonicmoe/quack_utils/blockscaled_fp8_gemm.py |
quant kernels, iso32, TMA reduce-add, FP8 GEMM wrappers |
| 12 | private env-notes file (SONIC_MOE_ENV_NOTES) |
machine, proxy, ncu lock reset, profiling methodology |
Historical but non-authoritative:
reports/fp8_upgrade/engineering_log.md: chronological lessons only (Sessions 1–66). It has a current-state correction block at the top.reports/fp8_upgrade/HANDOFF.md: stale historical reference.
GemmDGatedFP8CLoadSm100ZeroMat (file: sonicmoe/quack_utils/gemm_dgated.py:510-708) is the most complex kernel in the forward/backward pipeline. Its GEMM is only
-
FP8 C-Load via Int16 view trick: The saved
$z_{\text{fp8}}$ of shape$(M, 2I)$ isviewed as$(M, I)$ Int16. Each Int16 =[z_g_fp8, z_u_fp8]— two fp8 elements packed. This aligns C and D tile shapes for the CUTLASS epilogue framework without any framework changes. -
Blockscaled FP8 dequant in-register: UE8M0 scale bytes are left-shifted 23 bits and reinterpreted as f32 power-of-two values (
_i32_as_f32(Int32(scale) << 23)). This avoids any int-to-float conversion — exploits IEEE 754 biased exponent encoding directly. -
dSwiGLU fused at 9 SASS instructions per element pair:
1 MUFU.TANH + 4 FMUL + 4 FMA— algebraically minimal. The sigmoid is computed as0.5 * tanh(0.5*x) + 0.5via MUFU.TANH (¼ throughput of FMUL). -
Dual TMA store: Outputs
$\partial z_g$ and$\partial z_u$ as two FP8 streams + a BF16swiglu_outstream via separate TMA descriptors.
Why 168 regs/thread: C-load smem layout + Int16 recast registers + dequant scale registers + dSwiGLU intermediates + dual TMA store coordinates. Block Limit = 1 (only 1 CTA per SM). No headroom for epilogue dz quant loops.
Key profiled metrics: TC pipe busy ~42%, DRAM ~22.5%, L2 hit ~61%. This kernel is epilogue-bound, not compute-bound or memory-bound in isolation.
SonicMoE stores [gate_row_0, up_row_0, gate_row_1, up_row_1, ...] (interleaved) instead of the standard split-half [all gate rows, all up rows]. This is NOT a single-kernel optimization — it's a full-stack architectural decision with 5 cascading benefits:
- GEMM epilogue SwiGLU fusion — gate/up pair lands in adjacent accumulator registers → no cross-tile access needed
- FP8 Int16 view trick — physically adjacent
z_g, z_ubytes can beviewed as a single Int16 → enables the C-load mechanism - SwiGLU Triton kernel coalesced access — stride-2 pattern hits same cacheline for both gate and up
- Wgrad GEMM native output — CUTLASS wgrad naturally outputs interleaved → zero mid-iteration transpose
- FP8 blockscaled quantization symmetry — each 32-element scale group mixes gate+up → error averaging
The layout conversion to ERNIE's split-half happens only at node.step() time, amortized over grad_acc_steps microbatches. See docs/expert_interleave_weight_layout.md for full derivation.
This design is enabled by self-authored CUTLASS-DSL GEMM kernels (QuACK) with programmable epilogues — frameworks using black-box GEMM libraries cannot realize these benefits.
The newcomer guide Section 7 now contains the precise 6-GEMM FLOPs table and DGated real compute breakdown. Key insight: DGated's epilogue is only ~0.2% of its GEMM FLOPs, but causes ~15% TC pipe utilization loss because MUFU.TANH is ¼ throughput and 168 regs means no latency hiding.
Full MFU gap (100% → 46.5%):
- GEMM shape efficiency + varlen: ~46%
- Epilogue pipeline stall (DGated): ~15%
- Non-GEMM kernels: ~23%
- Fixed overhead: ~8%
- L2/DRAM contention: ~8%
F = 18 * TK * H * I
MFU = F / (busy_seconds * peak_FLOPs_per_second)
Reference:
TK = 8192 * 8 = 65536
F = 18 * 65536 * 3072 * 1536 = 5.566e12 FLOPs
ideal @4500 TFLOPS = 1237 µs
measured = 2659.8 µs
MFU = 46.51%
Fitted from multiple fresh sweep points, not one point:
busy_us =
18*TK*H*I / (4500e6 * eta_max)
+ a_quant * TK * max(H,2I) * 1e-9
+ a_expert * E * TK * 1e-6
+ c_fixed
eta_max = 0.541569
a_quant = 50.0
a_expert = 328.939
c_fixed = 201.047 µs
R^2 = 0.99896
Interpretation:
eta_maxis effective GEMM family efficiency, including shape/varlen/epilogue effects.a_quantis an empirical data-scale term, not the literal time of quant kernels.a_expertcaptures fragmentation and per-expert overhead as E increases.- Refit if any fusion/fission/communication overlap changes the system.
extra_bits_lost = log2(block_amax_32x32 / row_amax_1x32)
If p95 exceeds ~1.5 bits or max exceeds ~2.5 bits, consider fallback to 1x32 dual quant for that tensor/expert/step. Thresholds need training calibration; this is a guardrail, not a theorem.
Fuse when:
saved_HBM + saved_launch + saved_allocator/cache
>
extra_register_cost + lost_occupancy + lost_parallelism + extra_control
Fission when:
occupancy/tensor_pipe gain + reduced spill + better tile shape
>
extra HBM store/load + extra launch/grid sync + lost locality
Current application: GemmDGatedFP8CLoadSm100ZeroMat is a fission candidate because it has 168 regs/thread and only ~42% tensor-pipe utilization; direct epilogue fusion of dz quant is not the right next step.
- FP8 真实加速比是 1.63x(vs 真 BF16)。 之前报告的 “1.11x” 是由于 BF16 baseline 被 FP8 kernel 污染(SonicMoEMlpNode 强制 enable_fp8(True)),已在本次 session 修复。正确数据:FP8 2660 µs vs 真 BF16 4346 µs = 1.63x。历史 S53 cuBLAS BF16 = 3644 µs(当前 BF16 比 S53 慢 19%,原因是 Paddle proxy overhead + FP8 infra branch cost)。
- Do not try to directly add dz quant loops to GemmDGated epilogue. NCU shows 168 regs/thread × 384 threads = 64512/65536 regs, leaving effectively no register headroom.
- compute-sanitizer can mask register-limit crashes. Use it for memory safety, not as proof that a high-register kernel is production-safe.
- TMA reduce-add is performance, not higher precision. It avoids C-load/register pressure; determinism must still be tested.
- iso32 is measured-scope safe, not universally exact. Guard with amax-ratio/zero-ratio monitoring when changing distributions.
- compile_key must stay static. Dynamic token dimensions in compile keys cause recompile storms.
- Paddle proxy differs from PyTorch.
torch.equal, stream handles, dtype strings,_inplace_version, storage offsets, and bf16 conversions all need compatibility handling. - nsys and ncu answer different questions. End-to-end busy/MFU uses nsys GPU-projection; kernel resource bottlenecks use ncu SoL/register/L2/DRAM. Do not compare their durations directly unless clock/replay policy matches.
node.step()order is non-negotiable. It must precedeoptimizer.step().- Use whitelisted env for paddlejob launch. Denylist cleanup is unsafe; cluster env vars can silently force multi-node rendezvous.
- Never use
.numel()or.element_size()in hot-path Paddle proxy code. These trigger implicit cudaMemcpy D2H (GPU stream sync). Use.size(int property) and.itemsizeinstead. Gated bytests/test_no_memcpy_sync.py. See PR#22. - SonicMoEMlpNode 现在支持真 BF16 模式。 设置
SONIC_MOE_FP8_MODE=""即可。BF16 路径使用 GemmGatedSm100 + GemmDGatedSm100(CuTe DSL BF16 GEMM),wgrad 通过 gemm_add() 累加到 main_grad,与 FP8 路径共享 zero-materialization 和 varlen 基础设施。 - MXFP8 128 行对齐浪费是硅片硬约束,kernel 层面无解。 详细分析见私有 alignment-waste 笔记。TC:CUDA core 吞吐比 = 58.5:1,CUDA core 并发 tail 不可行(tail 必须 < 0.67% 才能隐藏)。系统级解法:token rounding(已实现未上线)或跨 microbatch buffer(可行,改善 = grad_acc_steps 倍)。
- Token Rounding 已实现但未进入生产。 代码在
forward_token_choice_rounding(Mtile=128),但因路由扰动对训练收敛的影响未验证,生产环境仍用 route-level padding。
Target: GemmDGatedFP8CLoadSm100ZeroMat — the worst per-FLOP kernel (42% TC pipe).
Facts:
- 168 regs/thread, near register cliff. Block Limit = 1.
- Tensor pipe ~42%, L2 hit ~61%, DRAM ~22.5%.
- Direct dz quant epilogue fusion is NOT viable (zero register headroom).
- The MFU loss from this kernel’s epilogue stall is ~15% of total gap.
- MUFU.TANH (sigmoid) is the SFU bottleneck: ¼ throughput of FMUL.
Promising directions (ranked by expected ROI):
- Live-range shortening in FP8 C-load + dSwiGLU epilogue — the Int16 recast path holds
tRS_rXY_f32x2and dequant scales across the full epilogue iteration. If scale reuse or register aliasing can free 4-8 regs, occupancy may jump to Block Limit = 2. - Fission: split GEMM mainloop from dSwiGLU+store — GEMM alone would be ~56 regs, full occupancy. dSwiGLU+store as a separate small kernel reading accumulator from smem/L2. Trade: +1 kernel launch + accumulator spill to smem (~12 MiB per tile). Needs careful modeling.
- C-load scheduling: prefetch scales while GEMM tiles still running — overlap scale TMA with mainloop; currently scales are loaded only in
epi_visit_subtile. - Sigmoid approximation — replace MUFU.TANH with polynomial approximation using FMUL/FMA (4× throughput). Risk: precision regression in dSwiGLU chain.
Inference megakernels don’t transfer to training. A realistic path:
- Stage-wise persistent pipeline (fwd1 → fwd2 → dgrad1 → dgrad2) with producer-consumer via smem/L2 — not a single-kernel solution, but a multi-kernel persistent occupancy scheme.
- Pre-requisite: dgrad1 occupancy improvement (P0) so it can share SMs with other kernels.
- Add optional runtime audits for iso32
log2(block_amax/row_amax)and zero ratio. - Keep frontier determinism hard-gated.
- Add tests around
node.step()ordering / repeated accumulation / cache invalidation.
- Keep
reports/sonic_moe_fp8_frontier_newcomer_guide.mdas the newcomer source (updated Section 7: MFU). - Keep
docs/gemm_dgated_fp8cload.mdanddocs/expert_interleave_weight_layout.mdfor deep technical reference. - Keep README aligned with this handoff.
- Treat
engineering_log.mdas history only.
source .runenv.sh
# Hard determinism gate
CUDA_VISIBLE_DEVICES=0 USE_QUACK_GEMM=1 \
python -m pytest tests/fp8_frontier_determinism_test.py -v
# Stress / routing robustness
CUDA_VISIBLE_DEVICES=0 USE_QUACK_GEMM=1 \
python -m pytest tests/fp8_frontier_stress_test.py -v
# Full regression when changing kernels or metadata
bash tests/run_regression.shFor profiling:
nsys profile --trace=cuda,nvtx --sample=none --backtrace=none \
--resolve-symbols=false --export=sqlite --output=OUTPUT \
python tests/ops/bench_mlpnode_topk_nsys.py --T 8192 --E 8 --I 1536 --topk 8If ncu exits abnormally and locks clocks, run:
ncu --clock-control=reset