44 KiB
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 移植的关键改动点
- UMMA → WGMMA:SM100 leader CTA 单 warp 发 UMMA,结果存 TMEM;SM90 每个 math WG(4 warps)独立发 WGMMA,结果驻留 WG register file
- TMEM epilogue → Register epilogue:SM100 epilogue WG 从 TMEM load;SM90 math WG 自身持有 accumulator,math 和 epilogue 在同一 WG 中串行
- FP4 weights → FP8 weights:weight HBM 读取量翻倍,是 SM90 bandwidth bottleneck 的根本原因
- UE8M0 SF → float SF:activation/weight SF 使用 32-bit float,layout 简化(无需 4×32 UTCCP transpose)
- 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:
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
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 三档选择
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
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
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
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 入口
// 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 层路由
# 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 开发。