1050 lines
44 KiB
Markdown
1050 lines
44 KiB
Markdown
# 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-load;cluster=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 必须使用 FP8,HBM 读取量翻倍 |
|
||
| 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,结果存 TMEM;SM90 每个 math WG(4 warps)独立发 WGMMA,结果驻留 WG register file
|
||
2. **TMEM epilogue → Register epilogue**:SM100 epilogue WG 从 TMEM load;SM90 math WG 自身持有 accumulator,math 和 epilogue 在同一 WG 中串行
|
||
3. **FP4 weights → FP8 weights**:weight HBM 读取量翻倍,是 SM90 bandwidth bottleneck 的根本原因
|
||
4. **UE8M0 SF → float SF**:activation/weight SF 使用 32-bit float,layout 简化(无需 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=64;single-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 load,cooperative 只 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-load,weight 读取减半
|
||
- 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 reference:dispatch → 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 count,pipeline stages 计算 | 给定 shape 参数验证输出 config 合理(stages ≥ 3,smem ≤ 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 下无 hang,pool 内容与 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 warp,WG1/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 warp:wait `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 warp:TMA load L1 FP8 weights to SMEM | dump SMEM B 区域对比 |
|
||
| 3.5 WGMMA descriptor | 构造 A/B GMMA descriptor;cooperative 下 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 累积 half0,K-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 cast,STSM 到 `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/half1(k_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_M,JIT 编译对应模板 | 正确选择 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 开发。
|