Files
DeepGEMM/MEGAMOE_SM90_DESIGN.md
2026-06-17 23:54:49 +08:00

1050 lines
44 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MegaMoE SM90 Implementation Design (Final)
## 1. Executive Summary
本文档描述 DeepGEMM MegaMoE 在 SM90 (Hopper: H100/H200/H20) 架构上的最终实现设计。设计基于对 SM100 fused kernel (`sm100_fp8_fp4_mega_moe.cuh`, PR304) 的详细分析,以及对三条现有 SM90 探索路线 (PR323 fused, PR352 split, PR360 pingpong/cooperative) 的横向对比研究。
### 1.1 核心设计决策
| # | 决策项 | 最终选择 | 理由 |
|---|--------|---------|------|
| 1 | Kernel 架构 | **Fused single kernel** | 单 kernel launch 减少 overhead代码简洁 (~1900 行) |
| 2 | Warpgroup 策略 | **Cooperative (BM=128) + single-WG (BM≤64)** | Cooperative 共享 weight load 主攻 HBM bottleneck小 M 退化为 single-WG 避免 compute 浪费 |
| 3 | BLOCK_M | **动态 128/64/32** | 按 expected_tokens_per_expert 三档选择,覆盖 decode 到 prefill |
| 4 | BLOCK_N | **固定 128** | 64 个 accumulator regs/WG寄存器压力可控 |
| 5 | Cluster | **固定 1** | Cooperative 已共享 B-loadcluster=2 的 amax 跨 CTA 同步过于复杂 |
| 6 | 线程布局 | **384 = 64 dispatch + 64 TMA + 256 math/epilogue** | 与 SM100 的 512 相比省出 WG 寄存器预算 |
| 7 | SF 策略 | **Exact float SF** | 与 PyTorch 标准 FP8 quantize 一致 |
| 8 | Weight SF 加载 | **Math WG `__ldg`** | 无额外 SMEM 占用PR323/360 已验证 |
| 9 | L2 Act SF | **Per-64 dual-half SF** | 精度更高PR323/360 采用 |
| 10 | L2 Scheduling | **Auto N-major** | 大 M 下减少 weight L2 cache thrash |
| 11 | Math WG Regs | **224 reg/warp** | 总占用 96.9%PR360 已验证可行 |
| 12 | L1 TMA Store | **Double-buffered** | TMA store 与 epilogue 计算重叠 |
| 13 | API | **独立 SM90 入口** | Python 层显式区分 SM90/SM100 |
| 14 | Combine | **复用 SM100 逻辑** | Chunked TMA combine 与 TMEM/UMMA 无关,可直接移植 |
| 15 | 文件组织 | **单文件 `sm90_fp8_mega_moe.cuh`** | 模板参数区分 cooperative/single-WG |
| 16 | Clamp + FastMath | **两者都支持** | 模板参数,零开销 |
---
## 2. SM100 vs SM90 Architecture Gap
### 2.1 硬件能力对比
| 能力 | SM100 (Blackwell) | SM90 (Hopper) | 影响 |
|------|-------------------|---------------|------|
| Tensor Memory (TMEM) | 有 | **无** | accumulator 必须驻留在 registers |
| UMMA (Unified MMA) | 有(`tcgen05` | **无** | 使用 WGMMA (`wgmma.mma_async`) |
| FP4 Tensor Core | 有E2M1 | **无** | weight 必须使用 FP8HBM 读取量翻倍 |
| UTCCP (SF transport) | 有 | **无** | SF 通过 TMA 或 `__ldg` 加载 |
| 2-CTA Cluster MMA | 有(`Allocator2Sm` | 不适用 | cluster 仅用于 TMA multicast |
| Scale Factor Format | UE8M0 packed `int` | **float** | per-128-K granularity, 32-bit |
| SMEM Capacity | 232 KB | 232 KB | 相同 |
| Registers/SM | 65,536 | 65,536 | 相同,但 TMEM 缺失使 register 压力剧增 |
| WGMMA Shape | N/A (UMMA) | M=64, N=8..256, K=32 (FP8) | 固定 M=64 per warpgroup |
### 2.2 从 SM100 移植的关键改动点
1. **UMMA → WGMMA**SM100 leader CTA 单 warp 发 UMMA结果存 TMEMSM90 每个 math WG4 warps独立发 WGMMA结果驻留 WG register file
2. **TMEM epilogue → Register epilogue**SM100 epilogue WG 从 TMEM loadSM90 math WG 自身持有 accumulatormath 和 epilogue 在同一 WG 中串行
3. **FP4 weights → FP8 weights**weight HBM 读取量翻倍,是 SM90 bandwidth bottleneck 的根本原因
4. **UE8M0 SF → float SF**activation/weight SF 使用 32-bit floatlayout 简化(无需 4×32 UTCCP transpose
5. **2-CTA cluster allocation → single CTA**SM90 cluster 固定为 1不做 2-SM MMA 协同
---
## 3. Kernel Architecture: Fused Single Kernel
### 3.1 设计选型理由
选择 **fused single kernel** 而非 dual-kernel 或 split-kernel
| 方案 | 优势 | 劣势 | 结论 |
|------|------|------|------|
| **Fused single kernel** ✓ | 单次 launch无 overhead代码简洁 | 无法独立优化 L1/L2 的线程配置 | **选定** |
| Dual-kernel auto-routing | 各 kernel 独立优化 | 两套 ~1400 行 kernel 维护负担 | 不选 |
| Split L1/L2 | L1/L2 独立 register budget | Kernel launch overhead无 pipeline overlap | 不选 |
### 3.2 模板参数驱动的 Warpgroup 模式
通过模板参数 `kCooperativeMode` 在编译期选择 cooperative 或 single-WG
```cpp
template <..., bool kCooperativeMode, ...>
void sm90_fp8_mega_moe_impl(...) {
// kCooperativeMode=true: BLOCK_M=128, 两个 math WG M-split
// kCooperativeMode=false: BLOCK_M=64/32, 只有 WG1 工作, WG2 空闲
}
```
| BLOCK_M | kCooperativeMode | Math WGs | 每 WG 行数 | 场景 |
|---------|-----------------|----------|-----------|------|
| 128 | true | 2 (cooperative) | 64 | 大 M prefill |
| 64 | false | 1 (single) | 64 | 中 M decode |
| 32 | false | 1 (single) | 32 (WGMMA M=64, 用 32 行) | 极小 M decode |
### 3.3 Fused Kernel Pipeline
单 kernel 覆盖 dispatch → L1 GEMM → SwiGLU → L2 GEMM → NVLink scatter → combine reduce
```
Phase 1: Dispatch
WG0 dispatch warps → count experts, write source metadata, grid sync
→ NVLink barrier → pull remote tokens/SF/weights into local L1 pool
→ l1_arrival_count release
Phase 2: Linear1 (L1 GEMM + SwiGLU)
WG0 TMA warps → wait l1_arrival_count, TMA load L1 acts/weights
WG1 (+ WG2 if cooperative) → WGMMA FP8×FP8, float SF scaling
→ SwiGLU epilogue in registers, top-k weight multiply
→ per-row amax, FP8 quantize, double-buffered TMA store l2_acts
→ write float l2_acts_sf, set l2_arrival_mask
Phase 3: Linear2 (L2 GEMM + NVLink scatter)
WG0 TMA warps → wait l2_arrival_mask, TMA load L2 acts/weights
WG1 (+ WG2 if cooperative) → WGMMA FP8×FP8, per-64 dual-half SF
→ BF16 convert, SMEM staging, NVLink scatter to remote combine buffer
Phase 4: Combine Reduce
NVLink barrier → math/epilogue warps TMA-load top-k BF16 chunks
→ FP32 accumulate → BF16 cast → TMA store output y
```
---
## 4. Thread Layout
### 4.1 384 Threads = 12 Warps = 3 Warpgroups
```
WG0 (128 threads = 4 warps):
w0-w1 (64 threads): Dispatch + NVLink pull + workspace cleanup
w2 (32 threads): TMA load A (activations + SFA)
w3 (32 threads): TMA load B (weights); weight SF 由 math WG ldg
WG1 (128 threads = 4 warps):
w4-w7: Math WG0 - WGMMA + epilogue
Cooperative: rows [0, 64)
Single-WG: rows [0, BLOCK_M)
WG2 (128 threads = 4 warps):
w8-w11: Math WG1
Cooperative: rows [64, 128)
Single-WG: idle (register-deallocated)
```
### 4.2 Register Allocation
| Role | Warps | Reg/Warp | Total |
|------|-------|----------|-------|
| WG0 (dispatch+TMA) | 4 | 48 | 6,144 |
| WG1 (math+epilogue) | 4 | 224 | 28,672 |
| WG2 (math+epilogue) | 4 | 224 | 28,672 |
| **Total** | 12 | — | **63,488 / 65,536 (96.9%)** |
Single-WG mode 下 WG2 同样分配 224 但不执行计算(寄存器仍被占用以保持 `__launch_bounds__` 一致性)。
---
## 5. WGMMA Pipeline
### 5.1 MMA Instruction
```
WGMMA FP8: MMA_64x128x32_F32E4M3E4M3_SS_TN
M = 64 (per warpgroup)
N = 128 (= BLOCK_N)
K = 32 (sub-step, 4 steps per BLOCK_K=128)
Accumulator: float[64] per WG (64×128/128 = 64 registers)
```
### 5.2 K-Loop Inner Pipeline
```
for k_block_idx in range(num_k_blocks):
// Wait TMA full barrier (SMEM A/B ready)
full_barriers[stage].wait(phase)
// WGMMA fence + commit
warpgroup_arrive()
for k_step in range(BLOCK_K / 32): // 4 steps
desc_a = advance_gmma_desc(smem_a[stage], wg_m_offset, k_step * 32)
desc_b = advance_gmma_desc(smem_b[stage], 0, k_step * 32)
WGMMA::wgmma(desc_a, desc_b, accum, k_step > 0 or k_block_idx > 0)
warpgroup_commit_batch()
warpgroup_wait<0>()
// Signal TMA empty barrier (SMEM consumed)
empty_barriers[stage].arrive()
advance_pipeline(stage, phase)
```
`wg_m_offset`cooperative mode 下 WG1=0, WG2=64single-WG mode 下 WG1=0。
### 5.3 Scale Factor Application
WGMMA 完成后在 registers 中软件 apply SF
```
// After WGMMA accumulation for a tile:
// 1. Load SFA (per-128 activation scale) from SMEM via ld_shared
// 2. Load SFB (per-128 weight scale) from global via __ldg (software prefetch)
// 3. Apply: accum[i] *= sfa[row_i] * sfb[col_i]
```
### 5.4 L2 Per-64 Dual-Half SF Scaling
L1 epilogue 对每 64 列 output 生成独立 float SF。L2 GEMM K=128 需要拆成两个 per-64 half
```
// L2 WGMMA K-loop produces raw accum over K=128
// After all K iterations:
// Load two per-64 SFA halves
sfa_half0 = l2_acts_sf[k_group_0 * stride + pool_token_idx] // k=[0,64)
sfa_half1 = l2_acts_sf[k_group_1 * stride + pool_token_idx] // k=[64,128)
// Apply: need to track partial sums per half during K-loop
// Option A: Two separate WGMMA passes (K=64 each), scale, add
// Option B: Single K=128 pass, then decompose using SF ratio
//
// Chosen: Option A (two K=64 passes with intermediate scaling)
// K-step 0..1 → accum_half0, scale by sfa_half0 * sfb
// K-step 2..3 → accum_half1, scale by sfa_half1 * sfb
// final = accum_half0 + accum_half1
```
---
## 6. Cooperative Mode (BLOCK_M=128)
### 6.1 M-Split Layout
```
smem_A [128 rows × BLOCK_K]:
WG1 → desc points to rows [0, 64)
WG2 → desc points to rows [64, 128)
smem_B [BLOCK_N × BLOCK_K]:
SHARED - TMA loads ONCE, both WGs consume
smem_cd [128 rows × output_cols]:
WG1 → writes rows [0, 64)
WG2 → writes rows [64, 128)
```
### 6.2 TMA Load
```
TMA A warp (w2): load full 128-row A tile
→ full_barriers[stage].arrive_and_expect_tx(SMEM_A_SIZE)
TMA B warp (w3): load BLOCK_N-row B tile (shared)
→ full_barriers[stage].arrive_and_expect_tx(SMEM_B_SIZE)
```
Weight HBM 读取量:相比 single-WG 的两次独立 B loadcooperative 只 load 一次 → **weight HBM 减半**
### 6.3 L1 Epilogue Sync
两个 WG 各自完成自己 64 行的 SwiGLU + FP8 quant + TMA store 后:
```
// 等两个 WG 的 TMA store 都完成
tma_store_wait<0>()
sync_aligned(256, kEpilogueFullBarrierIdx)
// 一个 WG 代表设置 l2_arrival_mask
if (epilogue_warp_idx == 0 and elect_one_sync())
red_or_rel_gpu(l2_arrival_mask[pool_block_idx], 1 << n_block_idx)
```
### 6.4 smem_cd Aliasing Guard
L1 (FP8) 和 L2 (BF16) 的 smem_cd 复用同一 SMEM 区域。L2 epilogue scatter 完成前不得进入下一 tile
```
// L2 epilogue 末尾:
sync_aligned(256, kEpilogueFullBarrierIdx) // 等所有 WG scatter 完成
```
---
## 7. Single-WG Mode (BLOCK_M=64/32)
### 7.1 退化行为
当 BLOCK_M ≤ 64 时,`kCooperativeMode=false`
- **WG1** 执行全部 WGMMA + epilogue 工作
- **WG2** 在 kernel 开始后立即 `warpgroup_reg_dealloc` 并 idle
- TMA A warp 只加载 BLOCK_M 行(而非 128 行)
- TMA B warp 不变
### 7.2 BLOCK_M=32 的 WGMMA 适配
WGMMA M 固定为 64但 BLOCK_M=32 时只有 32 个有效行。处理方式:
- WGMMA 仍计算 64×128 的 tile但只有 accumulator 中前 32 行的结果被 epilogue 使用
- epilogue 中 `valid_m = min(num_tokens_in_block, BLOCK_M)` 控制实际写入行数
- 计算利用率 50%,但在 token-per-expert ≤ 16 的极端 decode 场景下 tile 数量少,总 compute 量不大
---
## 8. Shared Memory Layout
### 8.1 SMEM Regions
```
smem_buffer (232 KB total, 1024-byte aligned):
┌──────────────────────────────────────────────────────┐
│ Expert Count (kNumExperts × 4B, align to 1024B) │ Dispatch region
├──────────────────────────────────────────────────────┤
│ Send Buffers (num_dispatch_warps × hidden, aligned) │
├──────────────────────────────────────────────────────┤
│ smem_cd: max(L1 FP8 double-buf, L2 BF16 staging) │
│ L1: store_block_m × (BN/2) × sizeof(FP8) × 2 │
│ L2: BLOCK_M × BN × sizeof(BF16) │
├──────────────────────────────────────────────────────┤
│ smem_a[0..num_stages-1]: │ Pipeline stages
│ BLOCK_M × BLOCK_K × sizeof(FP8) per stage │
├──────────────────────────────────────────────────────┤
│ smem_b[0..num_stages-1]: │
│ BLOCK_N × BLOCK_K × sizeof(FP8) per stage │
├──────────────────────────────────────────────────────┤
│ smem_sfa[0..num_stages-1]: │ Float SF
│ align(2 × BLOCK_M × sizeof(float), 128) per stage │
├──────────────────────────────────────────────────────┤
│ Amax reduction scratch │
├──────────────────────────────────────────────────────┤
│ Barriers + combine barriers │
└──────────────────────────────────────────────────────┘
```
### 8.2 Pipeline Stage Budget
```
Cooperative (BLOCK_M=128, BLOCK_N=128, BLOCK_K=128):
smem_a = 128 × 128 × 1B = 16 KB
smem_b = 128 × 128 × 1B = 16 KB
smem_sfa = align(256 × 4B, 128) = 1 KB
Total per stage ≈ 33 KB
Fixed ≈ 42 KB (expert_count + send_buffers + smem_cd + amax + barriers)
Available = 232 - 42 = 190 KB
Num stages = 190 / 33 ≈ **5 stages**
Single-WG (BLOCK_M=64):
smem_a = 64 × 128 × 1B = 8 KB
smem_b = 128 × 128 × 1B = 16 KB
smem_sfa = align(128 × 4B, 128) = 512 B
Total per stage ≈ 24.5 KB
Num stages = 190 / 24.5 ≈ **7 stages**
Single-WG (BLOCK_M=32):
smem_a = 32 × 128 × 1B = 4 KB
Total per stage ≈ 20.5 KB
Num stages = 190 / 20.5 ≈ **9 stages**
```
---
## 9. Dispatch Phase
### 9.1 与 SM100 的差异
| 项目 | SM100 | SM90 |
|------|-------|------|
| Dispatch threads | 128 (WG0 全部) | **64** (WG0 w0-w1) |
| Register budget | 48 reg/warp | 48 reg/warp (相同) |
| SF copy | UTCCP 4×32 transpose 写入 l1_sf_buffer | **直接按自然 layout 写入** (无 transpose) |
| Token pull | TMA load 1D + TMA store 1D | 相同 |
### 9.2 Dispatch Pull Path
```
for each assigned token (round-robin across global warps):
// Round-robin rank selection via iterative min-peeling (same as SM100)
select source rank
// TMA load token from remote rank into SMEM send buffer
tma_load_1d(pull_buffer, remote_token_ptr, pull_mbarrier, kHidden)
// Load float SF from remote rank (direct copy, no UTCCP transpose)
for each sf_element:
local_sf[k_group * stride + pool_token_idx] = remote_sf[...]
// Load top-k weight, wait TMA, store token via TMA store 1D
...
// Write source metadata + signal block ready
token_src_metadata[pool_token_idx] = {rank, token_idx, topk_idx}
red_add_rel(l1_arrival_count[pool_block_idx], 1)
```
---
## 10. L1 Epilogue: SwiGLU + FP8 Quantization
### 10.1 Register-Based Flow
SM90 L1 epilogue 在 math WG 自身的 registers 中执行:
```
// After WGMMA completes for a L1 tile:
// accum[64] holds FP32 values
// 1. Apply SF: accum[i] *= sfa[row_i] * sfb[col_i]
// 2. Load top-k weight for each row
topk_weight = l1_topk_weights[pool_token_idx + row_offset]
// 3. SwiGLU: silu(gate) * up * topk_weight
// Gate/up pairs interleaved in N dimension (granularity 8)
for each gate-up pair:
if kActivationClamp != inf:
gate = clamp(gate, -clamp, clamp)
up = clamp(up, -clamp, clamp)
silu_gate = gate / (1 + (kFastMath ? __expf(-gate) : expf(-gate)))
result = silu_gate * up * topk_weight
// 4. Per-row amax reduction across 4 warps in WG
amax = warp_reduce<4>(max(abs(result_values)))
// 5. Compute exact float SF, quantize to FP8 E4M3
sf = amax / 448.0
fp8_result = cast_to_fp8(result / sf)
// 6. Write float SF to l2_acts_sf (MN-major, per-64-K layout)
l2_acts_sf[k_idx * stride + pool_token_idx] = sf
// 7. STSM to smem_cd, double-buffered TMA store to l2_acts
stsm(smem_cd[tma_stage], fp8_result)
tma_store_2d(tensor_map_l1_output, smem_cd[tma_stage], n_idx, m_idx)
// 8. Signal L2 ready
red_or_rel_gpu(l2_arrival_mask[pool_block_idx], 1 << n_block_idx)
```
### 10.2 Cooperative L1 Epilogue
- WG1 处理 rows [0, 64), WG2 处理 rows [64, 128)
- 各自独立计算 amax、SF、FP8 quant、TMA store
- `sync_aligned(256, kEpilogueFullBarrierIdx)` 等两个 band 都完成后设置 `l2_arrival_mask`
---
## 11. L2 Epilogue: BF16 Scatter + Combine
### 11.1 L2 BF16 Epilogue
```
// After WGMMA with dual-half SF scaling:
// 1. Cast FP32 → BF16, STSM to smem_cd_l2
bf16_values = float_to_bf16(final_accum)
stsm(smem_cd_l2, bf16_values)
// 2. sync WG-internal
sync_aligned(128, kEpilogueWGBarrierStartIdx + wg_idx)
// 3. Read source metadata, NVLink scatter
for each valid row:
metadata = token_src_metadata[m_idx + row_offset]
packed = ld_shared(smem_cd_l2 + row * BLOCK_N * sizeof(BF16))
*sym_buffer.map(combine_dst_ptr, metadata.rank_idx) = packed
// 4. Cooperative: sync_aligned(256) guard smem_cd alias
sync_aligned(256, kEpilogueFullBarrierIdx)
```
### 11.2 Combine Reduce (复用 SM100 逻辑)
NVLink barrier 后epilogue warps 执行 chunked TMA-based top-k reduce
```
// Per warp processes one token at a time
for token_idx in stride(sm_idx * num_epi_warps + warp_idx, num_tokens,
num_sms * num_epi_warps):
// Double-buffered TMA load all active top-k BF16 chunks
// Accumulate in float registers
float2 reduced[chunk_elems] = {0}
for each active slot:
tma_load_1d(load_buffer[stage], combine_chunk_ptr, barrier, chunk_bytes)
barrier.wait()
for each element:
reduced[i] += bf16_to_float2(load_buffer[stage][i])
flip stage
// Cast to BF16, TMA store to output y
tma_store_1d(y + token_idx * hidden + chunk_offset, store_buffer, chunk_bytes)
```
---
## 12. Scheduler
### 12.1 Wave-Based Expert Scheduling
复用 SM100 `MegaMoEScheduler` 核心设计,移除 2-CTA cluster 约束:
- `block_idx = blockIdx.x`SM100: `blockIdx.x` 隐含 2-CTA 配对)
- L1/L2 block N counts 不再要求偶数
- `kClusterSize = 1` 固定
Wave 结构不变:每 wave 处理 `kNumExpertsPerWave` 个 expert先遍历 L1 blocks 再回到 wave 起点遍历 L2 blocks。
### 12.2 Auto N-Major
```cpp
bool use_n_major_l2 = (expected_tokens_per_expert >= 256);
// 可通过 DG_SM90_MOE_NMAJOR 覆盖: -1=auto, 0=off, 1=on
// M-major: block_idx → (m_block, n_block) = (idx / num_n_blocks, idx % num_n_blocks)
// N-major: block_idx → (m_block, n_block) = (idx % num_m_blocks, idx / num_m_blocks)
```
---
## 13. Heuristics
### 13.1 BLOCK_M 三档选择
```cpp
static auto get_block_config_for_sm90_mega_moe(
int num_ranks, int num_experts, int num_max_tokens_per_rank,
int num_topk, int num_tokens) {
float expected = float(num_tokens) * num_ranks * num_topk / num_experts;
if (expected <= 16.5) {
// 极小 decode: RL long-tail, 极大 EP
return {.block_m = 32, .cooperative = false, .store_block_m = 16};
} else if (expected <= 64.5) {
// 中等 decode
return {.block_m = 64, .cooperative = false, .store_block_m = 32};
} else {
// 大 M prefill / 大 EP decode
return {.block_m = 128, .cooperative = true, .store_block_m = 32};
}
}
```
### 13.2 Wave Count
```cpp
static int get_num_experts_per_wave(...) {
// Same logic as SM100:
// 1. Estimate L1 blocks per expert
// 2. Find smallest value whose total blocks keep all SMs busy
// 3. Round up to nearest divisor of num_experts_per_rank
}
```
### 13.3 Pipeline Stages
```cpp
static auto get_pipeline_config(...) {
constexpr int smem_capacity = 232448; // SM90
// Fixed regions: expert_count + send_buffers + smem_cd + amax + barriers
// Per-stage: smem_a + smem_b + smem_sfa + 2 barriers
int num_stages = (smem_capacity - smem_fixed) / smem_per_stage;
assert(num_stages >= 3);
return {num_stages, total_smem};
}
```
---
## 14. Synchronization Map
### 14.1 Barrier Inventory
| Barrier | Type | Count | Usage |
|---------|------|-------|-------|
| `dispatch_barriers` | `ClusterTransactionBarrier` | 2 (dispatch warps) | Dispatch pull TMA mbarrier |
| `full_barriers` | `ClusterTransactionBarrier` | num_stages | TMA→math: SMEM data ready |
| `empty_barriers` | `ClusterTransactionBarrier` | num_stages | Math→TMA: SMEM consumed |
| `combine_barriers` | `ClusterTransactionBarrier` | num_epi_warps × 2 | Combine TMA double-buffer |
### 14.2 Intra-SM Named Barriers
| ID | Name | Scope | Usage |
|----|------|-------|-------|
| 0 | `kDispatchBarrierIdx` | 64 dispatch threads | Dispatch internal phases |
| 1 | `kDispatchWithEpilogueBarrierIdx` | 64 + 256 threads | Dispatch↔epilogue handshake |
| 2 | `kEpilogueFullBarrierIdx` | 256 epilogue threads | L1/L2 cooperative sync + smem_cd alias guard |
| 3+ | `kEpilogueWGBarrierStartIdx` | 128 threads (per WG) | WG-local smem_cd write sync |
### 14.3 Cross-Phase Sync
| Sync Point | Mechanism | Producer | Consumer |
|------------|-----------|----------|----------|
| Dispatch → L1 TMA | `l1_arrival_count` release-add | Dispatch warp | TMA A warp |
| L1 Epilogue → L2 TMA | `l2_arrival_mask` bit-OR release | Math WG | TMA A warp |
| L2 Scatter → Combine | NVLink barrier | All epilogue threads | All epilogue threads |
| Combine → Cleanup | `kDispatchWithEpilogueBarrierIdx` | Epilogue | Dispatch |
---
## 15. Weight Transform
### 15.1 SM90 Weight Transform
```python
def transform_weights_for_mega_moe_sm90(l1_weights, l2_weights):
"""
SM90 weight transform:
- L1: gate/up FP8 weight interleave along N (granularity 8)
- L2: no transform
- Weight SF: natural MN-major float layout, NO UTCCP transpose
"""
E, N, K = l1_weights.shape # N = 2 * intermediate_hidden
half_N = N // 2
interleaved = torch.empty_like(l1_weights)
for i in range(0, half_N, 8):
interleaved[:, 2*i:2*i+8, :] = l1_weights[:, i:i+8, :] # gate
interleaved[:, 2*i+8:2*i+16, :] = l1_weights[:, half_N+i:half_N+i+8, :] # up
return interleaved, l2_weights
```
### 15.2 Weight SF Layout
```
SM100: UE8M0 packed int, UTCCP 4×32 transposed, per-32-K
SM90: float, natural MN-major, per-128-K
Shape: (num_experts, N/128, K/128) float32
Kernel indexing: sf[expert * (N/128 * K/128) + n_block * (K/128) + k_block]
```
---
## 16. API Design
### 16.1 独立 SM90 入口
```cpp
// csrc/apis/sm90_mega.hpp (新文件)
static void fp8_mega_moe(
const torch::Tensor& y,
const std::tuple<torch::Tensor, torch::Tensor>& l1_weights_tuple,
const std::tuple<torch::Tensor, torch::Tensor>& l2_weights_tuple,
const std::optional<torch::Tensor>& cumulative_local_expert_recv_stats,
const torch::Tensor& sym_buffer,
const std::vector<int64_t>& sym_buffer_ptrs, const int& rank_idx,
const int& num_max_tokens_per_rank,
const int& num_experts, const int& num_topk,
const std::tuple<int, int, int>& recipe, // (1, 1, 128) for FP8
const std::string& activation,
const std::optional<float>& activation_clamp_opt,
const bool& fast_math
);
static void register_sm90_apis(pybind11::module_& m) {
m.def("get_token_alignment_for_sm90_mega_moe", ...);
m.def("get_symm_buffer_size_for_sm90_mega_moe", ...);
m.def("fp8_mega_moe", &fp8_mega_moe);
m.def("transform_weights_for_mega_moe_sm90", ...);
}
```
### 16.2 Python 层路由
```python
# deep_gemm/mega/__init__.py
def fp8_mega_moe(y, l1_weights, l2_weights, ...):
if _is_sm90():
_C.fp8_mega_moe(...)
elif _is_sm100():
_C.fp8_fp4_mega_moe(...)
else:
raise RuntimeError("Unsupported architecture")
def transform_weights_for_mega_moe(l1_weights, l2_weights):
if _is_sm90():
return _C.transform_weights_for_mega_moe_sm90(...)
else:
return _C.transform_weights_for_mega_moe(...) # SM100 FP4+UTCCP
```
---
## 17. File Layout
```
deep_gemm/include/deep_gemm/
impls/
sm90_fp8_mega_moe.cuh # Fused kernel (cooperative + single-WG via template)
scheduler/
sm90_mega_moe.cuh # SM90 scheduler (no 2-CTA, N-major support)
csrc/
jit_kernels/
heuristics/
sm90_mega_moe.hpp # SM90 config: BLOCK_M selection, wave, stages
impls/
sm90_fp8_mega_moe.hpp # JIT runtime: template instantiation, TMA desc, launch
apis/
sm90_mega.hpp # SM90-only API: fp8_mega_moe, buffer alloc, pybind11
deep_gemm/
mega/__init__.py # Extended: SM90 routing + transform
tests/
test_mega_moe_sm90.py # SM90 correctness + benchmark
```
---
## 18. Environment Variables
| Variable | Values | Default | Description |
|----------|--------|---------|-------------|
| `DG_SM90_MOE_NMAJOR` | `-1\|0\|1` | `-1` (auto) | L2 N-major scheduling |
| `DG_SM90_MOE_PHASE_PROFILE` | `0\|1` | `0` | 启用 per-phase nsys profiling |
---
## 19. Known Risks & Mitigations
### 19.1 Register Budget (P0)
63,488 / 65,536 = 96.9% 利用率。余量 2,048 registers。
- 严格 `__launch_bounds__(384, 1)` + `warpgroup_reg_alloc<224>` / `warpgroup_reg_dealloc<48>`
- Per-64 dual-half SF 在 L2 中需要维护两组 partial accumulator —— 需要仔细编写避免 spill
- 若 224 不够可降至 208 (总 60,416, 92.2%)
### 19.2 HBM Bandwidth Bottleneck (P0)
SM90 FP8 weights 比 SM100 FP4 大 2×NCU 显示 ~90% DRAM utilization。
- Cooperative kernel 共享 B-loadweight 读取减半
- Auto N-major L2 scheduling 改善 weight tile L2 cache 命中率
- 未来可考虑 cluster=2 TMA multicast需解决 amax 跨 CTA 同步)
### 19.3 Single-WG BLOCK_M=32 Compute 浪费 (P2)
WGMMA M=64 但只用 32 行50% compute 浪费。在极端 decode (token-per-expert ≤ 16) 场景下可接受,因为总 compute 量极小。若需进一步优化,未来可考虑 `mma.sync` 路径PR352 已探索)。
### 19.4 NCU Profiling Limitations (P1)
Symmetric memory + NVLink barrier 与 NCU kernel replay 冲突。
- 使用 `nsys profile` + NVTX markers 做 timeline 分析
- 单 rank 模式 (num_ranks=1) 下可用 NCU
- `DG_SM90_MOE_PHASE_PROFILE` 分段计时
---
## 20. Correctness Verification
### 20.1 Baseline
PyTorch FP32/BF16 referencedispatch → L1 dequant GEMM → SwiGLU → FP8 quant → L2 dequant GEMM → combine。
### 20.2 Test Matrix
| Test | Tokens | Experts | Hidden × Inter | TopK | Ranks |
|------|--------|---------|----------------|------|-------|
| Smoke | 1-4 | 8 | 4096×2048 | 6 | 2 |
| Decode | 16-128 | 48 | 7168×2048 | 6 | 8 |
| Prefill | 256-8192 | 48 | 7168×3072 | 6 | 8 |
| DSV4 Flash | 16-8192 | 256 | 4096×2048 | 6 | 8 |
| DSV4 Pro | 16-8192 | 384 | 7168×3072 | 6 | 8 |
| MiMo-V2.5 | 16-8192 | 256 | 4096×2048 | 8 | 8 |
| Edge: 0 tokens | 0 | any | any | any | 2 |
### 20.3 Tolerance
- BF16 output: `allclose(atol=0.05, rtol=0.1)`
---
## 21. 工程实现 Phases
### Phase 0: 环境搭建与 Baseline 验证 (Day 1-3)
**目标**:建立开发环境、确认 SM100 fused kernel 可正常运行、准备 SM90 PyTorch reference baseline。
| 步骤 | 交付物 | 验证标准 |
|------|--------|----------|
| 0.1 在 H200 集群搭建开发环境 | 可编译运行的 DeepGEMM 环境 | `tests/test_mega_moe.py` SM100 path 通过 |
| 0.2 编写 PyTorch FP32/BF16 reference | `tests/test_mega_moe_sm90.py` baseline 函数 | 与 SM100 kernel output 在 BF16 精度内一致 |
| 0.3 搭建 SM90 单 rank 测试框架 | 可在 H200 上以 `__CUDA_ARCH__=900` 编译的 stub kernel | JIT 编译通过kernel launch 不崩溃 |
| 0.4 建立 nsys profiling 脚本 | `scripts/run_nsys_mega_moe_sm90.sh` | 能生成 timeline report |
**依赖**:无
**风险**PyTorch symmetric memory API 在特定 NCCL/CUDA 版本下 multicast 不可用 → 使用 `NCCL_NVLS_ENABLE=0` fallback
---
### Phase 1: 基础设施层 (Day 4-10)
**目标**:完成 kernel 外围的所有 C++/Python 基础设施,使 JIT 编译框架可以实例化空 kernel 并正确分配 buffer。
| 步骤 | 文件 | 交付物 | 验证标准 |
|------|------|--------|----------|
| 1.1 SM90 Scheduler | `deep_gemm/include/deep_gemm/scheduler/sm90_mega_moe.cuh` | 移除 2-CTA 约束的 scheduler支持 N-major | 单元测试:给定 expert token counts 和 SM 数,验证 block 分配覆盖所有 expert tiles |
| 1.2 SM90 Heuristics | `csrc/jit_kernels/heuristics/sm90_mega_moe.hpp` | BLOCK_M 三档 (32/64/128)wave countpipeline stages 计算 | 给定 shape 参数验证输出 config 合理stages ≥ 3smem ≤ 232KB |
| 1.3 JIT Runtime | `csrc/jit_kernels/impls/sm90_fp8_mega_moe.hpp` | Template instantiation 代码生成、TMA descriptor 创建、kernel launch | 空 kernel 可 JIT 编译 + launch立即 return |
| 1.4 API & Buffer | `csrc/apis/sm90_mega.hpp` | `fp8_mega_moe``get_symm_buffer_size_for_sm90_mega_moe`、pybind11 注册 | Python 可调用 `_C.get_symm_buffer_size_for_sm90_mega_moe(...)` |
| 1.5 Weight Transform | Python `mega/__init__.py` 扩展 | `transform_weights_for_mega_moe_sm90` (L1 gate/up interleave) | 对比 SM100 的 interleave 结果在 FP8 数据维度一致 |
| 1.6 Python Routing | `deep_gemm/mega/__init__.py` | `_is_sm90()` 检测 + 路由到 SM90 API | 在 H200 上 import deep_gemm 并调用 SM90 路径不报错 |
**验证里程碑**`python -c "import deep_gemm; deep_gemm.get_symm_buffer_for_mega_moe(...)"` 在 H200 上正确返回 buffer views。
**依赖**Phase 0 完成
**关键参考**
- SM100 对应文件:`csrc/jit_kernels/heuristics/mega_moe.hpp` (heuristics)、`csrc/jit_kernels/impls/sm100_fp8_fp4_mega_moe.hpp` (JIT)
- SM90 GEMM 参考:`csrc/jit_kernels/heuristics/sm90.hpp` (arch spec)
---
### Phase 2: Dispatch + L1 Pool 填充 (Day 11-15)
**目标**:只实现 dispatch 数据路径,不进入 TMA/WGMMA。验证 expert count、token routing、source metadata、跨 rank pull、`l1_arrival_count` 等 dispatch 产物正确,为后续 L1 GEMM 提供稳定输入。
| 步骤 | 交付物 | 验证标准 |
|------|--------|----------|
| 2.1 Dispatch-only kernel 骨架 | `sm90_fp8_mega_moe.cuh` 中 WG0 的 64-thread dispatch 角色分配TMA/math WG 可 idle | 编译通过launch_bounds(384,1)launch 不崩溃 |
| 2.2 Dispatch barrier / workspace 初始化 | dispatch mbarrier、grid sync counter、expert count、arrival counter 初始化 | 连续两次调用无残留 workspace 污染 |
| 2.3 Expert count | 统计本 rank token/top-k 到 local experts 的 recv count并写入 workspace | expert token counts 与 PyTorch routing reference 一致 |
| 2.4 Source metadata | 写入 `token_src_metadata[pool_token_idx] = {rank, token_idx, topk_idx}` | metadata 与 routing reference 逐项一致 |
| 2.5 Token/SF/top-k pull | 将输入 token、float SFA、top-k weight 拉入 local L1 pool | 单 rank 下 `l1_acts` / `l1_acts_sf` / `l1_topk_weights` 与输入逐字节或逐值一致 |
| 2.6 Cross-rank dispatch smoke | NVLink barrier + remote pull 路径打通 | 2-rank / 8-rank 下无 hangpool 内容与 reference 一致 |
| 2.7 `l1_arrival_count` release | 每个 pool block dispatch 完成后 release-add arrival count | arrival count 达到 block 内 token 数TMA consumer 可安全等待 |
**验证里程碑**:单 rank 和多 rank 下 dispatch-only 输出 `l1_acts``l1_acts_sf``l1_topk_weights``token_src_metadata``l1_arrival_count` 与 PyTorch routing reference 一致;不要求任何 GEMM 计算。
**依赖**Phase 1 完成
**关键挑战**
- pool token index / expert block offset 计算必须与 scheduler 后续消费保持一致
- symmetric memory `sym_buffer.map` 与 NVLink barrier 的 phase 计数必须可连续调用
- debug dump 只能临时使用,必须按 AGENTS.md 加 `// DEBUG` / `# DEBUG` 并 stash
---
### Phase 3: L1 TMA + 单 tile WGMMA (Day 16-20)
**目标**:在 Phase 2 dispatch 产物上实现 L1 TMA/WGMMA pipeline**不含 epilogue**accumulator 可直接丢弃或写 debug buffer。验证 TMA A/SFA/B 加载、WGMMA FP8×FP8 累积、SF scaling、empty barrier 的正确性。
| 步骤 | 交付物 | 验证标准 |
|------|--------|----------|
| 3.1 TMA/math kernel 骨架 | WG0 w2/w3 作为 TMA A/B warpWG1/WG2 作为 math warpgroup | 编译通过launch_bounds(384,1) |
| 3.2 Full/empty barrier 初始化 | full/empty mbarrier per pipeline stage 初始化 + `__syncthreads` | 无 launch failure / deadlock |
| 3.3 TMA A/SFA load | w2 TMA A warpwait `l1_arrival_count`TMA load L1 acts + float SFA to SMEM | dump SMEM A/SFA与 global pool 数据逐字节/逐值对比 |
| 3.4 TMA B load | w3 TMA B warpTMA load L1 FP8 weights to SMEM | dump SMEM B 区域对比 |
| 3.5 WGMMA descriptor | 构造 A/B GMMA descriptorcooperative 下 WG1/WG2 指向不同 M offset | descriptor 地址和 swizzle 与 SM90 GEMM reference 一致 |
| 3.6 WGMMA pipeline | math WG`warpgroup_arrive`, 4× K-step `WGMMA::wgmma`, `warpgroup_commit_batch`, `warpgroup_wait<0>` | 单 tile accumulator 与 PyTorch FP32 GEMM 结果对比(未 scale |
| 3.7 SF scaling | accum *= sfa * sfb (sfb via `__ldg`) | Scale 后 accumulator 与 reference scaled GEMM 一致 |
| 3.8 Empty barrier | WGMMA 完成后 arrive empty_barrier → TMA 可重用 SMEM | 多 K-block pipeline 不死锁 |
**验证里程碑**:单 expert、BLOCK_M=128 的 L1 GEMM无 epilogue输出与 PyTorch `(dequant(x) @ dequant(w).T) * sfa * sfb` 在 FP32 accumulator 精度内 `allclose(atol=1e-2, rtol=0.05)`
**依赖**Phase 2 完成
**关键挑战**
- WGMMA descriptor 构造(参考 `deep_gemm/mma/sm90.cuh` 中的 `make_gmma_desc`
- Cooperative mode 下两个 WG 的 desc 指向 smem_a 的不同 64-row 偏移
- `warpgroup_arrive()` / `warpgroup_commit_batch()` / `warpgroup_wait<0>()` 的 fence 语义
---
### Phase 4: L1 Epilogue — SwiGLU + FP8 Quant (Day 21-30)
**目标**:在 Phase 3 的 WGMMA 结果上完成 L1 epilogue 全流程,输出正确的 FP8 `l2_acts` 和 float `l2_acts_sf`
| 步骤 | 交付物 | 验证标准 |
|------|--------|----------|
| 4.1 Top-k weight load | 从 `l1_topk_weights_buffer` load weight per row | 值与 dispatch 写入一致 |
| 4.2 SwiGLU 计算 | gate/up deinterleave + `silu(gate) * up * topk_weight` in registers | 与 reference SwiGLU 一致精度atol=0.02 |
| 4.3 Activation clamp | `kActivationClamp` 模板参数控制 clamp | clamp 后无超范围值 |
| 4.4 fast_math SwiGLU | `__expf` + `fast_rcp` 快速路径 | 与精确版相差 < 0.5% |
| 4.5 Per-row amax | `warp_reduce<4>(max(abs(...)))` 跨 WG 内 4 warps reduce | amax 值与 PyTorch `abs().max(dim=-1)` 一致 |
| 4.6 Cooperative amax | 两个 WG 独立 amax各自 64 行SMEM scratch 区域用于跨 warp reduce | 每行 amax 正确 |
| 4.7 FP8 quantize | `sf = amax / 448.0``fp8 = cast(result / sf)` | 反量化后与 reference 误差 < 1% |
| 4.8 Float SF write | 写入 `l2_acts_sf` (MN-major, per-64-K layout) | SF 值正确、layout 与 L2 TMA load 兼容 |
| 4.9 STSM + double-buf TMA store | STSM 到 `smem_cd[stage]`TMA store 到 `l2_acts` | `l2_acts` 内容与 reference FP8 quant output 一致 |
| 4.10 `l2_arrival_mask` 设置 | `red_or_rel_gpu(mask, 1 << n_block_idx)` | 单 expert 全部 L1 N-blocks 完成后 mask == expected |
| 4.11 Cooperative sync | `sync_aligned(256)` 等两个 WG 都 TMA store 完成后设置 mask | 无 race condition |
**验证里程碑**:单 expert 的 `l2_acts` (FP8) 和 `l2_acts_sf` (float) 与 reference `SwiGLU → FP8_quant` 一致。Multi-expert 场景下 `l2_arrival_mask` 正确触发。
**依赖**Phase 3 完成
**关键挑战**
- WGMMA register layout 到 gate/up 列的映射关系(参考 SM100 的 `SM100_TMEM_LOAD_16dp256b1x` 读取模式SM90 的 register layout 不同)
- Double-buffered TMA store 的 `tma_store_wait<1>` 与 smem_cd 复用
- 寄存器压力SwiGLU 中间变量 + amax + SF + FP8 cast 共享 224 reg/warp 的 math WG budget
---
### Phase 5: L2 GEMM + NVLink Scatter (Day 31-40)
**目标**L2 WGMMA pipeline + per-64 dual-half SF + BF16 epilogue + NVLink scatter 到 remote combine buffer。
| 步骤 | 交付物 | 验证标准 |
|------|--------|----------|
| 5.1 L2 TMA wait | TMA A warp wait `l2_arrival_mask == expected` | 正确等待 L1 所有 N-blocks 完成 |
| 5.2 L2 TMA load | TMA load `l2_acts` + 两个 per-64 SFA halves | SMEM 数据正确 |
| 5.3 L2 WGMMA dual-half | K-step 0-1 累积 half0K-step 2-3 累积 half1 | 两组 partial accum 正确 |
| 5.4 Dual-half SF apply | `final = half0 * sfa0 * sfb + half1 * sfa1 * sfb` | 与 reference L2 dequant GEMM 一致 |
| 5.5 Weight SF `__ldg` | Math WG prefetch L2 weight SF from global | 值正确 |
| 5.6 BF16 cast + STSM | FP32 → BF16 castSTSM 到 `smem_cd_l2` (BF16 swizzle) | SMEM 内容正确 |
| 5.7 Source metadata read | `token_src_metadata[pool_token_idx + row]` → {rank, token, topk} | 值与 dispatch 写入一致 |
| 5.8 NVLink scatter | `ld_shared` + `sym_buffer.map` + remote store | 单 rank 下本地 combine buffer 数据正确 |
| 5.9 Cooperative smem_cd guard | `sync_aligned(256)` 防止下一 tile 覆盖 smem_cd | 多 tile 序列无 data corruption |
| 5.10 Multi-expert persistent loop | Scheduler `for_each_block` 遍历多 expert 的 L1→L2 cycle | 全量 expert 输出正确 |
**验证里程碑**:多 expert、单 rank 下 `combine_token_buffer` 内容与 reference L2 GEMM BF16 输出一致。`l2_arrival_mask` 正确控制 L1→L2 依赖。
**依赖**Phase 4 完成
**关键挑战**
- Per-64 dual-half 需要在 K-loop 中区分 half0/half1k_step 0-1 vs 2-3需要两组 accumulator 或 intermediate scale → **224 reg budget 最紧张的地方**
- NVLink scatter 的 `sym_buffer.map` 在 SM90 上的行为验证(与 SM100 一致)
- smem_cd_l2 (BF16) 和 smem_cd_l1 (FP8) 的 aliasing — 同一 SMEM 地址
---
### Phase 6: Combine Reduce + 端到端 Fused (Day 41-48)
**目标**:完成 combine reduce、NVLink barrier、workspace 清理。实现完整端到端 fused kernel。
| 步骤 | 交付物 | 验证标准 |
|------|--------|----------|
| 6.1 NVLink barrier | Epilogue grid sync + cross-rank signal | 多 rank 下所有 rank 到达 barrier |
| 6.2 Dispatch↔Epilogue handshake | `kDispatchWithEpilogueBarrierIdx` sync | Dispatch 和 epilogue 阶段正确交替 |
| 6.3 Combine TMA load | Double-buffered TMA 从 combine buffer load BF16 chunks | Load 数据与 scatter 写入一致 |
| 6.4 FP32 accumulate | 逐 top-k slot 累加到 float registers | 累加结果与 reference `sum(topk_weights * expert_outputs)` 一致 |
| 6.5 BF16 cast + TMA store | Cast + TMA store 到 output `y` | `y` 与 reference 在 BF16 精度内一致 |
| 6.6 Workspace cleanup | Dispatch warps 清理 expert counts、arrival counters、metadata | 下次 kernel 调用前 workspace 已归零 |
| 6.7 NVLink cleanup barrier | 跨 rank 确认 workspace 清理完成 | 连续两次 kernel 调用无 data corruption |
| 6.8 端到端 correctness | 完整 `fp8_mega_moe(...)` 调用 | `test_mega_moe_sm90.py` Smoke test 通过 (2 ranks, 4096×2048, topk=6) |
**验证里程碑**2-rank Smoke test (4 tokens, 8 experts) `allclose(atol=0.05, rtol=0.1)` 通过。
**依赖**Phase 5 完成
**关键挑战**
- Combine 逻辑直接移植自 SM100 (与 TMEM/UMMA 无关),但需要确认 SM90 下 TMA 1D load/store 的行为
- NVLink barrier 的 phase 计数在多次 kernel 调用间的正确性
- Workspace 清理时序dispatch warps 与 epilogue warps 并行执行不同的清理任务
---
### Phase 7: Single-WG 变体 (BLOCK_M=64/32) (Day 49-55)
**目标**:实现 `kCooperativeMode=false` 路径,支持小 BLOCK_M 场景。
| 步骤 | 交付物 | 验证标准 |
|------|--------|----------|
| 7.1 模板分支 | `if constexpr (kCooperativeMode)` 控制 WG2 行为 | 两种模式均编译通过 |
| 7.2 WG2 idle | WG2 在 `kCooperativeMode=false``warpgroup_reg_dealloc` 后 idle | 无 deadlock / launch failure |
| 7.3 TMA A 调整 | TMA A 只加载 BLOCK_M 行64 或 32而非 128 | SMEM A 数据正确 |
| 7.4 WGMMA 有效行 | `valid_m = min(tokens_in_block, BLOCK_M)` 控制 epilogue 写入范围 | 无 OOB 写入 |
| 7.5 Scheduler 适配 | 动态 BLOCK_M 下 scheduler 的 block 分配和 pool_block_offset 正确 | 全量 expert 覆盖,无漏 tile |
| 7.6 Heuristics JIT 分发 | 根据 `expected_tokens_per_expert` 选择 BLOCK_MJIT 编译对应模板 | 正确选择 config 并编译 |
| 7.7 Small M correctness | BLOCK_M=64: 单 WG 处理完整 64 行 | Smoke test 通过 |
| 7.8 Extreme decode | BLOCK_M=32: token-per-expert=1-2 | 极端 decode 场景输出正确 |
**验证里程碑**BLOCK_M=64 和 BLOCK_M=32 的 Smoke test 通过。动态 BLOCK_M 选择逻辑根据 token count 正确切换。
**依赖**Phase 6 完成cooperative 端到端已验证)
**关键挑战**
- BLOCK_M=32 时 WGMMA 计算 64 行但只用 32 行的 register 浪费
- Single-WG mode 下 WG2 的 named barrier arrive 需要调整WG2 不参与 epilogue barrier
---
### Phase 8: 多 Rank 集成测试 (Day 56-62)
**目标**:在 8×H200 集群上验证完整 multi-rank 正确性和性能。
| 步骤 | 交付物 | 验证标准 |
|------|--------|----------|
| 8.1 Multi-rank launch | 8-rank torchrun 启动 MegaMoE kernel | 无 hang / crash |
| 8.2 Correctness sweep | Token sweep (1-8192) × Shape sweep × BLOCK_M 三档 | 全部 `allclose` 通过 |
| 8.3 Edge cases | 0 tokens, unbalanced routing, single-expert-per-token | 无 crash输出正确 |
| 8.4 Benchmark framework | `bench_mega_moe_sm90.py`latency, TFLOPS, HBM GB/s | 可生成 performance table |
| 8.5 nsys timeline | Phase profile: dispatch / L1 / L2 / combine 各阶段时间 | 识别主要 stall 来源 |
| 8.6 DeepEP v1 baseline | 对比 DeepEP dispatch + grouped GEMM + combine | 输出 speedup ratio |
| 8.7 Regression test | CI 集成到 `tests/test_mega_moe_sm90.py` | `pytest` 通过 |
**验证里程碑**8-rank × DSV4 Flash shape (4096×2048, E256, topk6)correctness 通过 + fused latency < 3000us (128 tokens)。
**依赖**Phase 7 完成
---
### Phase 9: 性能优化 (Day 63-75)
**目标**:基于 nsys timeline 分析,针对瓶颈进行优化迭代。
| 步骤 | 优化方向 | 预期收益 | 依据 |
|------|---------|---------|------|
| 9.1 N-major scheduling tuning | 调整 `tokens/expert >= 256` 阈值 | 大 M 下 weight L2 cache 命中率提升 | NCU L2 sector miss 数据 |
| 9.2 SFA 软件 prefetch | L2 per-64 SFA 的 `__ldg` prefetch 提前一个 K-block | 减少 math stall waiting on SF | nsys timeline 中 SF load 占比 |
| 9.3 Combine chunk 大小调优 | 根据 hidden dim 选择 1 vs 2 chunks | 减少 combine 阶段 TMA round-trip | combine 占总时间比例 |
| 9.4 Dispatch pull 优化 | 增加 dispatch warp 内的 token parallelism | 减少 dispatch phase 时间 | nsys dispatch 耗时 |
| 9.5 Register spill 消除 | 检查 SASS 中的 local memory access调整变量生命周期 | 消除 spill 带来的 DRAM 流量 | cuobjdump --dump-sass |
| 9.6 wave_count 调优 | 根据实测 per-shape 最优 experts_per_wave | 减少 tail effect | benchmark sweep |
| 9.7 Optional: cluster=2 探索 | TMA multicast B tile需解决 amax 跨 CTA sync | weight HBM 进一步减半 | 若 cooperative 的 HBM 仍是瓶颈 |
**验证里程碑**:相对 Phase 8 baseline主要 shape 上 latency 降低 10-20%。
---
### Phase 依赖关系图
```
Phase 0 (环境)
v
Phase 1 (基础设施)
v
Phase 2 (Dispatch)
v
Phase 3 (L1 TMA + WGMMA)
v
Phase 4 (L1 Epilogue)
v
Phase 5 (L2 GEMM + Scatter)
v
Phase 6 (Combine + 端到端)
├──────────────────┐
v v
Phase 7 (Single-WG) Phase 8 (多 Rank 集成)
│ │
└──────┬───────────┘
v
Phase 9 (性能优化)
```
### 时间线总结
| Phase | 天数 | 累计 | 核心交付 |
|-------|------|------|---------|
| 0 | 3 | 3 | 环境 + baseline |
| 1 | 7 | 10 | 基础设施 (可 JIT 编译) |
| 2 | 5 | 15 | Dispatch + L1 pool 正确 |
| 3 | 5 | 20 | L1 TMA + WGMMA 正确 |
| 4 | 10 | 30 | L1 SwiGLU + FP8 quant 正确 |
| 5 | 10 | 40 | L2 GEMM + NVLink scatter 正确 |
| 6 | 8 | 48 | 端到端 fused kernel 正确 (2-rank) |
| 7 | 7 | 55 | 动态 BLOCK_M 全变体正确 |
| 8 | 7 | 62 | 8-rank 集成 + benchmark |
| 9 | 13 | 75 | 性能调优完成 |
**总工期**:约 75 工作日10-11 周),含充分的验证和调试时间。关键路径是 Phase 2-6 的串行 kernel 开发。