FlashMLA 是 DeepSeek 开发的高性能注意力内核库,为 DeepSeek-V3 和 DeepSeek-V3.2-Exp 模型提供底层计算支持。在代码库中,除了已知的 V3.2 (V32) 架构外,还存在一个名为 model1 的配置类型。这个Model1会是新模型的代号吗? GitHub 仓库: deepseek-ai/FlashMLA
在 csrc/params.h 文件中,定义了一个枚举类型 ModelType:
enum class ModelType {
V32, // DeepSeek V3.2
MODEL1 // model1 配置
};
这意味着 FlashMLA 支持两种不同的模型架构。系统在运行时会根据输入张量的维度自动判断使用哪种模型类型,代码如下(csrc/api/sparse_decode.h:318-325):
ModelType model_type;
if (d_qk == 576) {
model_type = ModelType::V32;
} else if (d_qk == 512) {
model_type = ModelType::MODEL1;
} else {
TORCH_CHECK(false, "Unsupported d_qk: ", d_qk);
}
结论:model1 是一个头部维度为 512 的模型配置,而 V32 (V3.2) 的头部维度为 576。
详细对比
核心维度参数
从 csrc/sm100/decode/head64/config.h:33-43 可以看到两种模型的详细配置:
| 参数 | V32 | model1 | 含义 |
|---|---|---|---|
| D_Q | 576 | 512 | Query 头部维度 |
| D_K | 576 | 512 | Key 头部维度 |
| D_V | 512 | 512 | Value 头部维度 |
| D_NOPE | 512 | 448 | 非位置编码部分维度 |
| D_ROPE | 64 | 64 | 旋转位置编码维度 |
观察:model1 的总维度从 576 降至 512,其中 D_NOPE 从 512 降至 448,而 D_ROPE 保持 64 不变。这意味着 D_NOPE + D_ROPE = D_Q 在两种模型中都成立。
2 量化策略差异
从同一配置文件中可以看到:
| 参数 | V32 | model1 |
|---|---|---|
| QUANT_TILE_SIZE | 128 | 64 |
| NUM_SCALES_EACH_TOKEN | 4 | 8 |
这意味着:
- V32 每 128 个 FP8 值使用 1 个缩放因子
- model1 每 64 个 FP8 值使用 1 个缩放因子
更细粒度的量化块(64 vs 128)配合更多的缩放因子(8 vs 4),表明 model1 采用了不同的量化精度策略。
3 RoPE 处理方式
从配置文件中的注释(csrc/sm100/decode/head64/config.h:39,55)可以看到:
static constexpr bool V_HAVE_ROPE = MODEL_TYPE == ModelType::V32 ? false : true;
static constexpr int K_ROPE_SW = MODEL_TYPE == ModelType::V32 ? 64 : 128;
以及 csrc/sm100/decode/head64/config.h:172 的注释:
// RoPE part, dequantized. SW64 in v32 mode, SW128 in MODEL1 mode
这表明:
- V32 中 Value 不包含 RoPE,RoPE 部分存储在 SW64 内存区域
- model1 中 Value 包含 RoPE,RoPE 部分存储在 SW128 内存区域
KV Cache 详解
每个 Token 的存储大小
从 csrc/api/sparse_decode.h:290-295 可以看到两种模型的 KV Cache 存储格式:
if (d_qk == 576 && d_v == 512) {
// V3.2 style
bytes_per_token = 512 + 64*2 + (512/128)*4;
} else if (d_qk == 512 && d_v == 512) {
// MODEL1 style
bytes_per_token = 448 + 64*2 + (448/64)*1 + 1;
}
计算结果:
- V32: 512 + 128 + 16 = 656 bytes/token
- model1: 448 + 128 + 7 + 1 = 584 bytes/token (实际代码注释为 576,可能存在额外对齐)
存储结构分解
V32 格式 (656 bytes) :
- NoPE 量化部分: 512 bytes (512 × float8_e4m3)
- RoPE 部分: 128 bytes (64 × bfloat16 × 2,分别给 K 和 V)
- 缩放因子: 16 bytes (4 × float32)
model1 格式:
- NoPE 量化部分: 448 bytes (448 × float8_e4m3)
- RoPE 部分: 128 bytes (64 × bfloat16 × 2)
- 缩放因子: 8 bytes (7 × float8_e4m3,其中 1 个为填充)
- 额外填充: 1 byte
TMA 张量步长
从 csrc/sm100/decode/head64/config.h:41 可以看到:
static constexpr int TMA_K_STRIDE = MODEL_TYPE == ModelType::V32 ?
D_NOPE+2*D_ROPE+4*(D_NOPE/QUANT_TILE_SIZE) : D_NOPE+2*D_ROPE;
计算:
- V32: 512 + 128 + 4×(512/128) = 512 + 128 + 16 = 656
- model1: 448 + 128 = 576
TMA (Tensor Memory Accelerator) 是 NVIDIA Hopper 架构引入的张量内存加速器,这个步长定义了张量在内存中的跨距。
内存布局与内核实现
SM100 架构下的内存布局
从 csrc/sm100/decode/head64/config.h:52-55:
static constexpr int D_Q_SW128 = 512;
static constexpr int D_Q_SW64 = MODEL_TYPE == ModelType::V32 ? 64 : 0;
static_assert(D_Q_SW128 + D_Q_SW64 == D_Q);
这说明:
- 两种模型都使用 SW128 存储前 512 维
- V32 额外使用 SW64 存储剩余的 64 维
- model1 不使用 SW64(因为总维度就是 512)
内核实例化文件
FlashMLA 为 model1 实现了专门的 CUDA 内核:
SM90 架构 (setup.py:78-79):
"csrc/sm90/decode/sparse_fp8/instantiations/model1_persistent_h64.cu",
"csrc/sm90/decode/sparse_fp8/instantiations/model1_persistent_h128.cu",
SM100 架构 (setup.py:103):
"csrc/sm100/decode/head64/instantiations/model1.cu",
这些实例化文件的内容非常简洁,例如 model1_persistent_h64.cu:
#include "../splitkv_mla.cuh"
namespace sm90::decode::sparse_fp8 {
template void run_flash_splitkv_mla_fp8_sparse_kernel<ModelType::MODEL1, 64>(
const SparseAttnDecodeParams ¶ms);
}
RoPE 处理的架构差异
从 csrc/sm100/decode/head64/kernel.cuh:532-580 的注释可以看到关键差异:
V32 的 RoPE 处理:
// V3.2: RoPE behaves like an extra block with size 64, so we can do RoPE first
RoPE 作为独立的 64 维块,可以先处理。
model1 的 RoPE 处理:
// MODEL1: RoPE is the last 64 dims within the full 512 dim, which couples
// with the last 64 dim from the NoPE part when performing dual GEMM.
//
// logical view: |0|1|2|3|4|5|6|7| (where 7 is the RoPE part)
// dual gemm's view:
RoPE 耦合在 512 维内部,需要与 dual GEMM 配合处理。
测试配置与支持特性
支持的特性列表
从 csrc/api/sparse_decode.h:14-28 定义的特性枚举:
enum class DecodeFeatures : int {
HEAD_64, HEAD_128,
HEAD_DIM_576, HEAD_DIM_512,
V32_KVCACHE_FORMAT,
MODEL1_KVCACHE_FORMAT,
ATTN_SINK,
TOPK_LENGTH,
EXTRA_KVCACHE,
EXTRA_TOPK_LENGTH
};
观察:EXTRA_KVCACHE 和 EXTRA_TOPK_LENGTH 是 model1 支持的特性。
架构支持矩阵
从 csrc/api/sparse_decode.h:156-180 可以看到:
| 实现类 | 头数 | d_qk | 支持的 KV Cache 格式 |
|---|---|---|---|
| Decode_Sm90_Impl | 64/128 | 512/576 | V32, MODEL1 |
| Decode_Sm100_Head64_Impl | 64 | 512/576 | V32, MODEL1 |
| Decode_Sm100_Head64x2_Impl | 128 | 512/576 | V32, MODEL1 |
| Decode_Sm100_Head128_Impl | 128 | 512 | MODEL1 (仅) |
注意:Decode_Sm100_Head128_Impl 只支持 MODEL1_KVCACHE_FORMAT,不支持 V32。
model1 测试配置
从 tests/test_flash_mla_sparse_decoding.py:105-113 定义了 4 种 model1 配置:
| 配置 | h_q | topk | extra_topk | block_size | extra_block_size |
|---|---|---|---|---|---|
| CONFIG1 | 64 | 128 | 512 | 256 | 64 |
| CONFIG2 | 128 | 128 | 1024 | 256 | 64 |
| CONFIG3 | 64 | 128 | 1024 | 256 | 2 |
| CONFIG4 | 128 | 128 | 1024 | 256 | 2 |
所有配置都包含 extra_s_k=16384 和 extra_topk,表明 model1 专为需要额外 KV Cache 的场景设计。
技术总结
model1 的核心特征
基于代码分析,model1 具有以下特征:
- 更紧凑的维度:512 vs 576,是 2 的幂次,有利于内存对齐
- 更细粒度的量化:64 vs 128 的量化块大小
- 更多的缩放因子:8 vs 4
- 不同的 RoPE 处理:耦合在主维度内而非独立
- 支持额外 KV Cache:V32 不支持的特性
性能数据
从仓库 README 可以看到,稀疏 MLA 解码内核在 H800 SXM5 上:
- 使用 FP8 KV Cache 时达到 410 TFLOPS
- 在 B200 上达到 350 TFLOPS(未完全优化版本)
参考资料
-
FlashMLA GitHub 仓库: github.com/deepseek-ai…
-
核心代码文件:
csrc/params.h- ModelType 枚举定义csrc/api/sparse_decode.h- API 接口和特性定义csrc/sm90/decode/sparse_fp8/config.h- SM90 配置csrc/sm100/decode/head64/config.h- SM100 配置tests/test_flash_mla_sparse_decoding.py- 测试配置