DeepGEMM Python 层架构深度分析
分析对象:DeepGEMM v2.4.2
项目路径:/Users/backyes/work/triton/DeepGEMM
目标读者:AI 系统研究人员、高性能计算库设计者
1. 总体架构概览
DeepGEMM 是一个面向 NVIDIA GPU(SM90/H100、SM100/B200)的 FP8/BF16/FP4 GEMM 内核库,采用 Python 前端 + C++ JIT 编译后端 的混合架构。Python 层承担 API 暴露、张量布局变换、权重预处理、分布式对称内存管理以及测试基础设施;C++ 层通过 pybind11 暴露为单一扩展模块 _C,负责运行时分发、JIT 编译、TMA 对齐布局和内核启动。
1.1 架构分层图
graph TB
subgraph Python层
A[deep_gemm/__init__.py<br/>API 入口与命名空间聚合]
B[deep_gemm/mega<br/>Mega MoE 对称内存管理]
C[deep_gemm/utils<br/>布局/数学/分布式工具]
D[deep_gemm/testing<br/>Benchmark 与数值验证]
E[deep_gemm/legacy<br/>Triton A100 遗留内核]
end
subgraph C++ 扩展 _C
F[python_api.cpp<br/>PYBIND11_MODULE 注册]
G[apis/gemm.hpp<br/>GEMM 主机端逻辑]
H[apis/layout.hpp<br/>SF 布局变换]
I[apis/mega.hpp<br/>Mega MoE 缓冲区]
J[apis/runtime.hpp<br/>运行时控制]
K[apis/attention.hpp<br/>MQA logits]
L[jit/compiler.hpp<br/>NVRTC JIT 编译器]
M[jit/device_runtime.hpp<br/>设备运行时]
N[jit_kernels/impls<br/>SM90/SM100 内核实现]
end
A --> F
B --> F
C --> F
D --> A
E --> F
A --> B
A --> C
A --> D
A --> E
F --> G
F --> H
F --> I
F --> J
F --> K
G --> L
G --> M
G --> N
H --> N
I --> N
1.2 目录结构
DeepGEMM/
├── deep_gemm/ # Python 包
│ ├── __init__.py # 主入口,聚合全部公开 API
│ ├── mega/ # Mega MoE 分布式推理
│ │ └── __init__.py # SymmBuffer + 权重变换
│ ├── utils/ # 工具层
│ │ ├── __init__.py
│ │ ├── layout.py # TMA 对齐布局(C++ 调用)
│ │ ├── math.py # FP8/FP4 量化、缩放因子处理
│ │ └── dist.py # 分布式初始化与通信
│ ├── testing/ # 测试基础设施
│ │ ├── __init__.py
│ │ ├── bench.py # 基准测试框架
│ │ ├── numeric.py # 数值验证
│ │ └── utils.py # 测试装饰器
│ ├── legacy/ # A100 Triton 遗留内核
│ │ ├── __init__.py
│ │ ├── m_grouped_gemm.py
│ │ ├── a_fused_m_grouped_gemm.py
│ │ ├── a_fused_k_grouped_gemm.py
│ │ ├── b_fused_k_grouped_gemm.py
│ │ └── tune_options.py
│ └── include/ # 随包分发的 C++ 头文件
│ └── deep_gemm/ # 调度器、MMA、epilogue 等
├── csrc/ # C++ 扩展源码
│ ├── python_api.cpp # pybind11 模块注册入口
│ ├── apis/ # 各域主机端逻辑
│ ├── jit/ # JIT 编译、缓存、设备运行时
│ ├── jit_kernels/ # 内核实现与启发式规则
│ │ ├── impls/ # SM90/SM100 具体内核
│ │ └── heuristics/ # 运行时启发式
│ ├── indexing/ # 索引内核
│ └── utils/ # C++ 工具函数
├── setup.py # 构建系统
└── scripts/generate_pyi.py # 类型 stub 生成
2. 包结构与模块职责
2.1 模块层次图
graph LR
subgraph deep_gemm 包
P[__init__.py<br/>命名空间聚合]
M[mega<br/>Mega MoE]
U[utils<br/>工具函数]
T[testing<br/>测试基础设施]
L[legacy<br/>Triton 遗留]
end
P --> _C[ _C 扩展模块]
P --> M
P --> U
P --> T
P --> L
M --> U
M --> _C
U --> _C
L --> _C
2.2 各模块职责
| 模块 | 职责 | 关键设计 |
|---|---|---|
deep_gemm/__init__.py |
聚合所有公开符号,初始化运行时 | 延迟导入、环境变量默认值、向后兼容别名 |
deep_gemm/mega/ |
Mega MoE 分布式推理的 Python 端 | 对称内存 SymmBuffer、权重 interleaving/SF 变换 |
deep_gemm/utils/ |
布局对齐、FP8/FP4 量化、分布式工具 | 纯 Python 量化 + C++ 布局变换混合 |
deep_gemm/testing/ |
Benchmark、数值验证、测试装饰器 | CUDA Event 计时、L2 flush、Kineto profiler 集成 |
deep_gemm/legacy/ |
A100 上 Triton 编写的 BF16 grouped GEMM | Triton autotune、Config 过滤 |
3. API 设计模式
3.1 命名约定
DeepGEMM 的公开 API 采用 数据类型 + 操作 + 布局 的三段式命名:
{precision}_{operation}_{layout}_{variant}
示例解析:
| API | 含义 |
|---|---|
fp8_fp4_gemm_nt |
FP8 输入、FP4 权重的 NT(Non-Transpose × Transpose)GEMM |
m_grouped_fp8_gemm_nt_contiguous |
M 维度分组的 FP8 GEMM,contiguous 布局 |
m_grouped_fp8_gemm_nt_masked |
M 维度分组的 FP8 GEMM,masked 变体 |
k_grouped_bf16_gemm_tn_contiguous |
K 维度分组的 BF16 GEMM,TN 布局 |
cublaslt_gemm_nn |
cuBLASLt 封装的 NN 布局 |
fp8_fp4_mega_moe |
Mega MoE 完整前向内核 |
3.2 _C 扩展模式
Python 通过单一 C++ 扩展模块 _C 与底层交互,采用 "全量导入 + 条件回退" 模式:
# 核心运行时控制(始终可用)
from ._C import set_num_sms, get_num_sms, set_tc_util, get_tc_util, ...
# cuBLASLt 封装(始终可用,不依赖 TensorMap)
from ._C import cublaslt_gemm_nt, cublaslt_gemm_nn, ...
# JIT 编译内核(需 CUDA 12.1+ 和 TensorMap 支持)
try:
from ._C import fp8_fp4_gemm_nt, m_grouped_fp8_gemm_nt_contiguous, ...
except ImportError:
pass # CUDA < 12.1 时静默回退
# Mega MoE(需 PyTorch 分布式对称内存)
from .mega import SymmBuffer, get_symm_buffer_for_mega_moe, ...
# Legacy Triton(A100 专用,捕获所有异常)
try:
from . import legacy
except Exception as e:
print(f'Failed to load legacy: {e}')
这种设计使得同一份 Python 包可以在 不同 GPU 架构、不同 CUDA 版本 下自适应降级。
3.3 编译期条件宏
C++ 层通过三个关键宏控制功能可用性:
| 宏 | 含义 | 影响 |
|---|---|---|
DG_FP8_COMPATIBLE |
FP8 指令可用 | SM90+ |
DG_TENSORMAP_COMPATIBLE |
TMA(Tensor Memory Accelerator)可用 | CUDA 12.1+、SM90+ |
DG_JIT_USE_RUNTIME_API |
使用 CUDA Runtime API 替代 Driver API | 可选编译选项 |
这些宏在 python_api.cpp 中通过 #include 不同的 apis/*.hpp 头文件来实现功能注册。
4. C++ 绑定机制
4.1 pybind11 注册入口
csrc/python_api.cpp 是整个扩展的唯一入口,通过 PYBIND11_MODULE 注册:
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.doc() = "DeepGEMM C++ library";
deep_gemm::attention::register_apis(m);
deep_gemm::einsum::register_apis(m);
deep_gemm::hyperconnection::register_apis(m);
deep_gemm::gemm::register_apis(m);
deep_gemm::layout::register_apis(m);
deep_gemm::mega::register_apis(m);
deep_gemm::runtime::register_apis(m);
}
4.2 各域 API 注册
每个子域通过静态 register_apis(pybind11::module_& m) 函数注入:
| 域 | 头文件 | 注册内容 |
|---|---|---|
runtime |
apis/runtime.hpp |
set_num_sms、set_tc_util、set_pdl、init |
gemm |
apis/gemm.hpp |
全部 FP8/BF16/FP4 GEMM 变体 |
layout |
apis/layout.hpp |
SF 布局变换、对齐查询 |
mega |
apis/mega.hpp |
对称缓冲区大小计算、Mega MoE 前向 |
attention |
apis/attention.hpp |
MQA logits 计算 |
einsum |
apis/einsum.hpp |
Einsum 内核 |
hyperconnection |
apis/hyperconnection.hpp |
TF32 Hyperconnection |
4.3 运行时初始化
Python 在 __init__.py 末尾调用 _C.init() 注入关键路径:
_C.init(
os.path.dirname(os.path.abspath(__file__)), # 库根目录(定位头文件)
_find_cuda_home() # CUDA 安装路径
)
C++ 端 runtime::register_apis 中的 init 函数:
m.def("init", [&](const std::string& library_root_path, const std::string& cuda_home) {
Compiler::prepare_init(library_root_path, cuda_home);
KernelRuntime::prepare_init(cuda_home);
IncludeParser::prepare_init(library_root_path);
});
这触发了 JIT 编译器、内核运行时和 include 解析器的延迟初始化。
4.4 调用链示例:fp8_fp4_gemm_nt
sequenceDiagram
participant P as Python: deep_gemm.fp8_fp4_gemm_nt
participant G as C++: gemm::fp8_fp4_gemm_nt
participant L as C++: layout::transform_sf_pair_into_required_layout
participant I as C++: sm90_fp8_gemm_1d1d / sm100_fp8_fp4_gemm_1d1d
participant J as C++: JIT Compiler / NVRTC
P->>G: 调用 (a, b, d, c, recipe, compiled_dims)
G->>G: early_return 检查(m==0 or n==0 or k==0)
G->>G: get_major_type_ab 检查 A/B 布局
G->>G: check_ab_fp8_fp4 形状/类型断言
G->>L: 变换 SFA/SFB 为 TMA 对齐布局
L-->>G: 返回 transformed_sfa, sfb, gran_k_a, gran_k_b
alt arch_major == 9 (H100)
G->>I: sm90_fp8_gemm_1d1d(...)
else arch_major == 10 (B200)
G->>I: sm100_fp8_fp4_gemm_1d1d(...)
end
I->>J: JIT 编译或缓存命中
J-->>I: 返回内核函数
I->>I: 启动内核
4.5 GEMM API 默认参数设计
gemm.hpp 的 register_apis 展示了 Pythonic 的默认参数设计:
m.def("fp8_fp4_gemm_nt", &fp8_fp4_gemm_nt,
py::arg("a"), py::arg("b"), py::arg("d"),
py::arg("c") = std::nullopt,
py::arg("recipe") = std::nullopt,
py::arg("recipe_a") = std::nullopt,
py::arg("recipe_b") = std::nullopt,
py::arg("compiled_dims") = "nk",
py::arg("disable_ue8m0_cast") = false);
关键参数说明:
- a, b:std::pair<Tensor, Tensor> 即 (data, scaling_factor) 对
- c:可选累加器(支持 C/D 不同地址的 GEMM)
- recipe:(rm, rn, rk) 三维粒度,用于 SM90 的 1D2D 内核
- compiled_dims:JIT 编译的维度字符串,如 "nk" 或 "mn"
- disable_ue8m0_cast:禁用 UE8M0 缩放因子打包
5. Mega MoE Python 设计
5.1 架构概览
Mega MoE 是 DeepGEMM 针对 大规模 MoE 分布式推理 的完整内核,核心创新是利用 PyTorch 的 torch.distributed._symmetric_memory 实现跨节点的对称内存通信。
graph TB
subgraph Python Mega MoE
A[get_symm_buffer_for_mega_moe<br/>创建 SymmBuffer]
B[transform_weights_for_mega_moe<br/>权重预处理]
C[fp8_fp4_mega_moe<br/>前向计算]
end
subgraph SymmBuffer
D[x: FP8 输入令牌]
E[x_sf: 输入缩放因子]
F[topk_idx: TopK 索引]
G[topk_weights: TopK 权重]
H[l1_acts: L1 层激活]
I[l1_acts_sf: L1 缩放因子]
J[l2_acts: L2 层激活]
K[l2_acts_sf: L2 缩放因子]
end
subgraph C++ Mega
L[get_symm_buffer_size_for_mega_moe<br/>计算缓冲区布局]
M[fp8_fp4_mega_moe<br/>跨 rank 计算]
end
A --> D
A --> E
A --> F
A --> G
A --> H
A --> I
A --> J
A --> K
A --> L
C --> M
B --> C
5.2 SymmBuffer 设计
SymmBuffer 封装了一块跨进程共享的对称内存,通过 torch::from_blob 在同一块原始内存上创建多个 tensor view:
class SymmBuffer:
def __init__(self, group, num_experts, num_max_tokens_per_rank,
num_topk, hidden, intermediate_hidden,
use_fp8_dispatch=True, activation='swiglu'):
# 1. C++ 端计算所需字节数和切片函数
num_bytes, slice_input_buffers = _C.get_symm_buffer_size_for_mega_moe(...)
# 2. 分配对称内存
self.buffer = symm_mem.empty(num_bytes, dtype=torch.int8, device='cuda')
# 3. 跨 rank 同步(rendezvous)
self.handle = symm_mem.rendezvous(self.buffer, group=group)
# 4. 创建 tensor views
(self.x, self.x_sf, self.topk_idx, self.topk_weights,
self.l1_acts, self.l1_acts_sf, self.l2_acts, self.l2_acts_sf) = \
slice_input_buffers(self.buffer)
5.3 内存布局计算
C++ 端 get_symm_buffer_size_for_mega_moe 通过 layout::Buffer 和 layout::Data 构建精确布局:
Buffer Layout (按地址递增):
┌─────────────────────────────────┐
│ input_token_buffer (FP8) │ num_max_tokens_per_rank × hidden
├─────────────────────────────────┤
│ input_sf_buffer (INT) │ num_max_tokens_per_rank × hidden/128
├─────────────────────────────────┤
│ input_topk_idx_buffer (INT64) │ num_max_tokens_per_rank × num_topk
├─────────────────────────────────┤
│ input_topk_weights_buffer (FP32)│ num_max_tokens_per_rank × num_topk
├─────────────────────────────────┤
│ l1_token_buffer (FP8) │ num_max_pool_tokens × hidden
├─────────────────────────────────┤
│ l1_sf_buffer (INT) │ num_padded_sf_pool_tokens × hidden/128
├─────────────────────────────────┤
│ l1_topk_weights_buffer (FP32) │ num_max_pool_tokens × 1
├─────────────────────────────────┤
│ l2_token_buffer (FP8) │ num_max_pool_tokens × intermediate_hidden
├─────────────────────────────────┤
│ l2_sf_buffer (INT) │ num_padded_sf_pool_tokens × intermediate_hidden/128
├─────────────────────────────────┤
│ combine_token_buffer (BF16) │ num_topk × num_max_tokens_per_rank × hidden*2
└─────────────────────────────────┘
注意 x_sf 是 K-major 布局,而 l1_acts_sf 和 l2_acts_sf 是 M-major 布局(通过 torch::from_blob 的自定义 stride 实现)。
5.4 权重变换
transform_weights_for_mega_moe 对 L1/L2 权重做预处理,以匹配内核的 UTCCP(Unified Tensor Core Coherent Parallelism)要求:
def transform_weights_for_mega_moe(l1_weights, l2_weights):
# L1: gate/up interleaving + SF 转置
l1_interleaved = _interleave_l1_weights(l1_weights) # [gate:0..7, up:0..7, gate:8..15, up:8..15, ...]
l1_weights = (l1_interleaved[0], _transpose_sf_for_utccp(l1_interleaved[1]))
# L2: 仅 SF 转置
l2_weights = (l2_weights[0], _transpose_sf_for_utccp(l2_weights[1]))
return l1_weights, l2_weights
_interleave_l1_weights:将 gate 和 up 投影的权重按 8 行粒度交错排列,匹配 SwiGLU 融合计算的数据访问模式。_transpose_sf_for_utccp:将 MN-major 的缩放因子转置为 K-major,满足 TMA 对齐要求(4×32 → 32×4的块转置)。
5.5 对齐约束
get_symm_buffer_for_mega_moe 工厂函数确保 num_max_tokens_per_rank 对齐到 block_m:
block_m = _C.get_block_m_for_mega_moe(num_ranks, num_experts, num_max_tokens_per_rank, num_topk)
num_max_tokens_per_rank = align(num_max_tokens_per_rank, block_m)
这是对称内存内核正确性的硬性要求——每个 rank 处理的 token 数必须是内核块大小的整数倍。
6. 工具层设计
6.1 utils/layout.py:TMA 对齐布局
layout.py 是 Python 层对 C++ 布局变换的封装,分为两部分:
CUDA 12.1+ 专用(依赖 TensorMap):
from .._C import (
get_tma_aligned_size,
get_mn_major_tma_aligned_tensor,
get_mn_major_tma_aligned_packed_ue8m0_tensor,
get_k_grouped_mn_major_tma_aligned_packed_ue8m0_tensor
)
全版本可用:
from .._C import (
set_mk_alignment_for_contiguous_layout,
get_mk_alignment_for_contiguous_layout,
get_theoretical_mk_alignment_for_contiguous_layout,
)
注意这里的别名技巧:
get_m_alignment_for_contiguous_layout = get_mk_alignment_for_contiguous_layout
get_k_alignment_for_contiguous_layout = get_mk_alignment_for_contiguous_layout
这是因为 M 和 K 的对齐值由同一个底层 heuristics 决定。
6.2 utils/math.py:FP8/FP4 量化
math.py 提供完整的量化/反量化工具链:
| 函数 | 功能 |
|---|---|
per_token_cast_to_fp8 |
按 token 粒度量化到 FP8(E4M3),支持 UE8M0 |
per_channel_cast_to_fp8 |
按 channel 粒度量化 |
per_block_cast_to_fp8 |
按 2D block 粒度量化 |
per_custom_dims_cast_to_fp8 |
任意维度组合量化 |
per_token_cast_to_fp4 |
量化到 FP4(E2M1),带打包 |
transpose_packed_fp4 |
FP4 打包张量的转置 |
cast_back_from_fp4 |
FP4 反量化回高精度 |
ceil_to_ue8m0 |
将 FP32 指数上取整到 UE8M0 表示 |
pack_ue8m0_to_int |
将 UE8M0 打包为 int8 |
UE8M0 表示:DeepGEMM 使用 NVIDIA 提出的 UE8M0(Unsigned Exponent 8-bit, Mantissa 0-bit)作为 FP8 的缩放因子格式。其核心思想是用一个 FP32 的纯指数值(尾数为 0)来表示 scale factor,确保跨 GPU 的一致性。
def ceil_to_ue8m0(x):
bits = x.abs().float().view(torch.int)
exp = ((bits >> 23) & 0xFF) + (bits & 0x7FFFFF).bool().int()
return (exp.clamp(1, 254) << 23).view(torch.float)
FP4 E2M1 量化:使用 torch.bucketize 将值映射到 {0, 0.5, 1, 1.5, 2, 3, 4, 6} 这 8 个离散值。
def _quantize_to_fp4_e2m1(x):
ax = x.abs().clamp_max(6.0)
boundaries = torch.tensor([0.25, 0.75, 1.25, 1.75, 2.5, 3.5, 5.0])
idx = torch.bucketize(ax, boundaries)
code = idx.to(torch.uint8)
sign = (x < 0) & (idx != 0)
code = code | (sign.to(torch.uint8) << 3)
return code.view(torch.int8)
6.3 utils/dist.py:分布式工具
| 函数 | 功能 |
|---|---|
init_dist |
基于环境变量的 NCCL 进程组初始化 |
uneven_all_gather |
支持不等长维度的 all-gather(MoE 核心操作) |
dist_print |
带 barrier 的分布式安全打印 |
init_dist 的设计亮点是自动适配 PyTorch 版本:
sig = inspect.signature(dist.init_process_group)
params = {'backend': 'nccl', 'init_method': f'tcp://{ip}:{port}', ...}
if 'device_id' in sig.parameters: # PyTorch 2.3+ 支持
params['device_id'] = torch.device(f'cuda:{local_rank}')
uneven_all_gather 实现了 MoE 中关键的 token 重分布:每个 rank 持有不同数量的 token,需要先交换大小、pad 到统一长度、all-gather、再去除 padding。
7. Legacy vs Modern 设计对比
7.1 设计哲学差异
| 维度 | Legacy(Triton) | Modern(JIT) |
|---|---|---|
| 目标硬件 | A100(SM80) | H100(SM90)、B200(SM100) |
| 编程语言 | Triton DSL | 纯 C++ + NVRTC JIT |
| 编译时 | Triton 编译期 | 运行时 JIT |
| 数据类型 | BF16 为主 | FP8、FP4、BF16、TF32 |
| 内核特性 | 标准 GEMM | TMA、UTCCP、PDL、psum layout |
| 调优方式 | triton.autotune |
运行时 heuristics |
| 布局要求 | 简单的 contiguous | TMA-aligned、MN-major |
7.2 Legacy 内核的 Triton 设计
Legacy 内核使用 Triton 的标准编程模型:
@triton.autotune(configs=get_m_grouped_gemm_configs(), key=[])
@triton.jit
def m_grouped_bf16_gemm_contiguous_tl_impl(a_ptr, b_ptr, d_ptr, m_indices_ptr, ...):
pid = tl.program_id(axis=0)
# 标准 tile 分配
group_id = pid // num_pid_in_group
pid_m = first_pid_m + (pid % group_size_m)
pid_n = (pid % num_pid_in_group) // group_size_m
# 空 token 检查(batch_id < 0 时跳过)
batch_id = tl.load(m_indices_ptr + pid_m * BLOCK_SIZE_M)
if batch_id < 0:
tl.store(d_ptrs, tl.zeros(...))
return
# 标准 GEMM 计算循环
for k in range(0, K, BLOCK_SIZE_K):
a = tl.load(a_ptrs)
b = tl.load(b_ptrs)
accumulator = tl.dot(a, b, accumulator)
关键设计点:
- 空 token 跳过:m_indices 中 -1 表示空 token,直接写零返回,避免无效计算
- Config 过滤:tune_options.py 根据共享内存大小(A100 限制 166912 bytes)和 MK 对齐约束过滤配置
- NN 布局复用:nn 变体通过 b.mT 转置复用 nt 实现
7.3 Modern JIT 内核的 C++ 设计
Modern 内核通过 NVRTC 在运行时编译,核心优势:
- TMA(Tensor Memory Accelerator):硬件加速的张量加载,需要特定的 MN-major 对齐布局
- UTCCP:统一张量核相干并行,需要特殊的 SF 布局
- PDL(Predicated Data Launch):谓词化数据启动,减少分支开销
- psum layout:部分和布局,优化 MoE 的 token 累加
JIT 编译流程:
graph LR
A[Python 调用 _C.fp8_gemm_nt] --> B[C++ 主机端检查]
B --> C{编译缓存命中?}
C -->|是| D[直接启动内核]
C -->|否| E[NVRTC 编译]
E --> F[生成 PTX/SASS]
F --> G[加载内核]
G --> D
D --> H[执行]
8. 构建与安装管线
8.1 setup.py 工作流
graph TB
A[python setup.py install] --> B[CustomBuildPy.run]
B --> C[prepare_includes<br/>复制 third-party 头文件到 build 目录]
C --> D[generate_default_envs<br/>生成 envs.py 持久化环境变量]
D --> E[generate_pyi_file<br/>生成 _C.pyi 类型 stub]
E --> F[build_py.run<br/>标准 Python 构建]
F --> G{CUDAExtension 编译}
G --> H[nvcc 编译 python_api.cpp]
H --> I[链接 cudart, nvrtc]
I --> J[生成 _C.cpython-xxx.so]
8.2 环境变量控制
| 变量 | 默认值 | 作用 |
|---|---|---|
DG_SKIP_CUDA_BUILD |
0 |
跳过 CUDA 编译(纯 Python 安装) |
DG_FORCE_BUILD |
0 |
强制从源码构建 |
DG_USE_LOCAL_VERSION |
1 |
在版本号后附加 git revision |
DG_JIT_USE_RUNTIME_API |
0 |
使用 CUDA Runtime API 替代 Driver API |
DG_JIT_CACHE_DIR |
~/.deep_gemm |
JIT 编译缓存目录 |
DG_JIT_PRINT_COMPILER_COMMAND |
- | 打印编译命令 |
DG_JIT_CPP_STANDARD |
20 |
C++ 标准版本 |
DG_JIT_DEBUG |
0 |
调试模式 |
DG_JIT_WITH_LINEINFO |
0 |
生成行信息 |
DG_USE_NVIDIA_TOOLS |
0 |
与 Nsight 工具兼容模式 |
DG_COMM_KERNEL_DEBUG |
0 |
Mega MoE 通信调试(每次调用后清零缓冲区) |
8.3 Wheel 缓存机制
CachedWheelsCommand 实现了智能的 wheel 下载回退:
class CachedWheelsCommand(_bdist_wheel):
def run(self):
if DG_FORCE_BUILD or DG_USE_LOCAL_VERSION:
return super().run() # 强制构建
try:
# 尝试从 GitHub Release 下载预编译 wheel
urllib.request.urlopen(wheel_url, timeout=1)
# 下载成功,直接使用
except (HTTPError, URLError):
print('Precompiled wheel not found. Building from source...')
super().run() # 回退到源码构建
Wheel 命名规则:
deep_gemm-{version}+cu{cuda}-torch{torch}-cxx11abi{abi}-{python}-{platform}.whl
8.4 Include 路径管理
prepare_includes 将 third-party 头文件复制到构建目录,避免污染源码树:
def prepare_includes(self):
build_include_dir = os.path.join(self.build_lib, 'deep_gemm/include')
for d in third_party_include_dirs: # cute, cutlass
shutil.copytree(src_dir, os.path.join(build_include_dir, dirname))
package_data 确保这些头文件随 wheel 分发:
package_data={
'deep_gemm': [
'include/deep_gemm/**/*',
'include/cute/**/*',
'include/cutlass/**/*',
]
}
8.5 编译器标志
cxx_flags = [
'-std=c++17', '-O3', '-fPIC',
'-Wno-psabi', '-Wno-deprecated-declarations',
f'-D_GLIBCXX_USE_CXX11_ABI={int(torch.compiled_with_cxx11_abi())}'
]
关键是与 PyTorch 的 CXX11 ABI 兼容性——通过 torch.compiled_with_cxx11_abi() 自动匹配。
9. 类型 Stub 生成
9.1 generate_pyi.py 工作原理
scripts/generate_pyi.py 是一个 独立的 C++ 签名提取器,无需 Clang/libclang 即可工作:
graph TB
A[扫描 csrc/ 下所有 .cpp/.hpp] --> B[build_cpp_function_index<br/>提取所有函数签名]
A --> C[extract_m_def_statements<br/>提取 m.def(...) 注册]
C --> D[parse_m_def_statement<br/>解析 Python 函数名、C++ 函数名、默认参数]
D --> E[parse_mdef_and_attach_cpp_signatures<br/>关联 C++ 签名]
E --> F[extract_cpp_signature_details<br/>解析返回类型和参数]
F --> G[generate_pyi_function<br/>生成 Python stub]
G --> H[写入 _C.pyi]
9.2 BracketTracker
核心组件 BracketTracker 跟踪四种括号的嵌套深度,确保在模板参数(<>)中正确分割:
class BracketTracker:
def __init__(self):
self.paren = 0 # ()
self.bracket = 0 # []
self.brace = 0 # {}
self.angle = 0 # <> # 仅在顶层时计数
9.3 C++ 到 Python 类型映射
cpp_type_to_python_type 实现了完整的类型转换:
| C++ 类型 | Python 类型 |
|---|---|
torch::Tensor |
torch.Tensor |
std::pair<T1, T2> |
tuple[T1, T2] |
std::tuple<T1, T2, ...> |
tuple[T1, T2, ...] |
std::vector<T> |
list[T] |
std::optional<T> |
Optional[T] |
std::string / char* |
str |
bool |
bool |
int / size_t / int64_t |
int |
float / double |
float |
void |
None |
9.4 默认值转换
cpp_default_to_python_default 处理 C++ 默认值的 Python 化:
| C++ 默认值 | Python 默认值 |
|---|---|
true / false |
True / False |
nullptr / NULL / nullopt |
None |
std::tuple<int,int>({128,128}) |
(128, 128) |
std::make_tuple(1, 2, 3) |
(1, 2, 3) |
std::vector<int>({1,2,3}) |
[1, 2, 3] |
10. 测试架构
10.1 测试模块组织
testing/
├── __init__.py # 聚合 bench, numeric, utils
├── bench.py # 基准测试框架
├── numeric.py # 数值验证
└── utils.py # 测试装饰器
10.2 bench.py:双模式基准测试
提供两种 benchmark 模式:
bench(fn) — 简单计时模式:
1. L2 flush:分配 256MB 张量并清零
2. Warmup:默认 5 次
3. 高精度模式:执行一次大 GEMM 消除 CPU launch overhead
4. 计时:CUDA Event 计时,默认 10 次取平均
bench_kineto(fn, kernel_names) — Kineto profiler 模式:
1. 8GB L2 flush(更激进的缓存清除)
2. torch.profiler 调度:wait=0, warmup=1, active=1, repeat=1
3. 解析 profiling 表格提取内核时间
4. 支持 Chrome trace 导出
关键设计:
- DG_USE_NVIDIA_TOOLS 环境变量检测,与 Nsight 工具兼容
- suppress_stdout_stderr 上下文管理器抑制 profiler 输出
- barrier 参数支持多 rank 同步
10.3 numeric.py:数值验证
def calc_diff(x, y):
"""计算两个张量的差异度(1 - 余弦相似度的变体)"""
x, y = x.double(), y.double()
denominator = (x * x + y * y).sum()
if denominator == 0:
return 0.0
sim = 2 * (x * y).sum() / denominator
return 1 - sim
这个指标是 1 - 归一化点积相似度,对于完全相同的张量返回 0,对于相反张量返回 2。
count_bytes 递归计算张量列表的字节数,用于带宽利用率计算。
10.4 utils.py:测试装饰器
| 装饰器 | 功能 |
|---|---|
test_filter(condition) |
条件跳过测试(如 lambda: torch.cuda.get_device_capability()[0] >= 9) |
ignore_env(name, condition) |
临时移除环境变量后执行测试 |
11. 环境变量持久化机制
11.1 envs.py 生成
CustomBuildPy.generate_default_envs 在构建时将当前环境变量固化:
def generate_default_envs(self):
code = '# Pre-installed environment variables\n'
code += 'persistent_envs = dict()\n'
for name in ('DG_JIT_CACHE_DIR', 'DG_JIT_PRINT_COMPILER_COMMAND', 'DG_JIT_CPP_STANDARD'):
code += f"persistent_envs['{name}'] = '{os.environ[name]}'\n" if name in os.environ else ''
with open(os.path.join(self.build_lib, 'deep_gemm', 'envs.py'), 'w') as f:
f.write(code)
11.2 运行时加载
__init__.py 在导入时应用这些默认值:
try:
from .envs import persistent_envs
for key, value in persistent_envs.items():
if key not in os.environ: # 用户设置优先
os.environ[key] = value
except ImportError:
pass
这确保了 构建时的配置在运行时自动生效,同时允许用户通过环境变量覆盖。
12. 设计模式总结
12.1 核心设计模式
| 模式 | 应用 | 效果 |
|---|---|---|
| 命名空间聚合 | __init__.py 统一导入 |
用户只需 import deep_gemm |
| 条件导入 | try/except ImportError 包裹 |
跨硬件/驱动版本自适应 |
| C++ 扩展封装 | 单一 _C 模块 |
清晰的 Python/C++ 边界 |
| 延迟初始化 | _C.init() 在导入末尾 |
避免 CUDA 过早初始化 |
| 环境变量持久化 | envs.py 生成 + 加载 |
构建配置自动传递到运行时 |
| 工厂函数 | get_symm_buffer_for_mega_moe |
封装对齐约束和复杂构造 |
| 双模式测试 | bench / bench_kineto |
快速验证 vs 精确 profiling |
| 类型 Stub 生成 | generate_pyi.py |
零依赖的 IDE 类型提示 |
12.2 架构优势
- 硬件自适应:同一份代码在 A100(Legacy Triton)、H100(SM90 JIT)、B200(SM100 JIT)上自动选择最优路径
- 版本容错:CUDA < 12.1 时自动回退到 cuBLASLt,不中断导入
- 零配置部署:wheel 缓存 + 环境变量持久化减少用户配置负担
- 可调试性:丰富的环境变量开关(
DG_JIT_DEBUG、DG_COMM_KERNEL_DEBUG等) - 类型安全:自动生成的
.pyi提供完整的 IDE 补全
12.3 潜在改进点
- Legacy 内核的长期维护:注释明确提到 "may be deprecated or rewrite in TileLang"
- Python 层量化性能:
math.py中的 FP8/FP4 量化是纯 Python 实现,可能成为瓶颈 - 错误信息:部分 C++ 断言(
DG_HOST_ASSERT)在 Python 层可能难以调试 - Wheel 缓存的 1 秒超时:
timeout=1在网络较慢时可能导致不必要的源码构建
13. 关键调用链总结
13.1 标准 FP8 GEMM 调用链
sequenceDiagram
participant User
participant Init as __init__.py
participant CExt as _C (python_api.cpp)
participant Gemm as gemm.hpp
participant Layout as layout.hpp
participant JIT as JIT Compiler
participant GPU
User->>Init: import deep_gemm
Init->>CExt: _C.init(library_root, cuda_home)
CExt->>JIT: prepare_init()
User->>Init: deep_gemm.fp8_gemm_nt(a, b, d)
Init->>Gemm: fp8_fp4_gemm_nt(a, b, d, ...)
Gemm->>Gemm: early_return() check
Gemm->>Layout: transform_sf_pair_into_required_layout()
Layout-->>Gemm: transformed_sfa, sfb
Gemm->>JIT: sm90_fp8_gemm_1d1d() or sm100_fp8_fp4_gemm_1d1d()
JIT->>GPU: launch kernel
GPU-->>User: result in d
13.2 Mega MoE 调用链
sequenceViewer
participant User
participant Mega as mega/__init__.py
participant Symm as symm_mem
participant CExt as _C (mega.hpp)
participant GPU
User->>Mega: get_symm_buffer_for_mega_moe(...)
Mega->>CExt: get_symm_buffer_size_for_mega_moe()
CExt-->>Mega: num_bytes, slice_func
Mega->>Symm: symm_mem.empty(num_bytes)
Mega->>Symm: symm_mem.rendezvous(buffer)
Symm-->>Mega: handle
User->>Mega: transform_weights_for_mega_moe(l1, l2)
Mega-->>User: transformed weights
User->>Mega: fp8_fp4_mega_moe(y, w1, w2, sym_buffer)
Mega->>CExt: fp8_fp4_mega_moe(...)
CExt->>GPU: sm100_fp8_fp4_mega_moe()
GPU-->>User: result in y
14. 文件索引
| 文件 | 行数 | 核心职责 |
|---|---|---|
deep_gemm/__init__.py |
127 | 包入口、API 聚合、运行时初始化 |
deep_gemm/mega/__init__.py |
129 | SymmBuffer、权重变换、Mega MoE 前向 |
deep_gemm/utils/__init__.py |
4 | 工具模块聚合 |
deep_gemm/utils/layout.py |
22 | TMA 对齐布局 C++ 封装 |
deep_gemm/utils/math.py |
143 | FP8/FP4 量化、UE8M0 处理 |
deep_gemm/utils/dist.py |
75 | NCCL 初始化、uneven all-gather |
deep_gemm/testing/__init__.py |
4 | 测试模块聚合 |
deep_gemm/testing/bench.py |
147 | 双模式 benchmark 框架 |
deep_gemm/testing/numeric.py |
22 | 数值差异度计算 |
deep_gemm/testing/utils.py |
38 | 条件过滤装饰器 |
deep_gemm/legacy/__init__.py |
3 | Legacy 模块聚合 |
deep_gemm/legacy/m_grouped_gemm.py |
86 | Triton BF16 M-GEMM |
deep_gemm/legacy/a_fused_m_grouped_gemm.py |
93 | Triton A-Fused M-GEMM |
deep_gemm/legacy/tune_options.py |
29 | Triton autotune 配置 |
csrc/python_api.cpp |
29 | pybind11 模块注册 |
csrc/apis/runtime.hpp |
52 | 运行时控制 API |
csrc/apis/gemm.hpp |
716 | GEMM 主机端逻辑与注册 |
csrc/apis/layout.hpp |
144 | SF 布局变换与注册 |
csrc/apis/mega.hpp |
217 | Mega MoE 缓冲区与注册 |
csrc/apis/attention.hpp |
~100+ | MQA logits 计算 |
csrc/jit/compiler.hpp |
~200+ | NVRTC JIT 编译器 |
csrc/jit/device_runtime.hpp |
~100+ | 设备运行时 |
csrc/jit/cache.hpp |
32 | 内核运行时缓存 |
setup.py |
215 | 构建系统 |
scripts/generate_pyi.py |
891 | 类型 stub 生成器 |
15. 结论
DeepGEMM 的 Python 层设计体现了 "薄 Python 前端 + 厚 C++ 后端" 的高性能库典型架构。Python 层的核心价值在于:
- API 可用性:通过命名空间聚合和条件导入提供统一的跨硬件接口
- 预处理逻辑:FP8/FP4 量化、权重变换等操作在 Python 层以声明式方式实现
- 分布式抽象:
SymmBuffer封装了复杂的对称内存管理 - 测试基础设施:与 PyTorch profiler 深度集成的 benchmark 框架
- 构建时配置:环境变量持久化机制确保构建-运行一致性
C++ 层通过 pybind11 暴露为单一 _C 模块,保持了 Python/C++ 边界的清晰。JIT 编译器和运行时 heuristics 使得同一份代码能自适应不同 GPU 架构,这是 DeepGEMM 在性能可移植性上的关键创新。