SGLang DeepSeek-V4 注意力测试能力矩阵深度解析:SWA / C4 / C128 压缩比模式与后端约束验证
【免费下载链接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.项目地址: https://gitcode.com/GitHub_Trending/sg/sglang
本指南以 SGLang 仓库中
test/registered/attention/unittests/dsv4/README.md为核心,全面讲解 DeepSeek-V4 注意力后端的测试覆盖设计:包括dsv4单一注意力后端在compress_ratio ∈ {0, 4, 128}三种模式下的能力矩阵、输入与配置覆盖策略、独立 PyTorch 参考实现原理,以及被明确判定为"生产不可达"(production-unreachable)的组合。读者读完可以掌握 DSV4 注意力测试的完整方法论,并能在仓库中准确找到后端实现、测试用例与测试工具链的对应位置。
为什么 DSV4 注意力测试需要独立的目录
DeepSeek-V4(下文简称 DSV4)的注意力机制与仓库中其他模型(dense、MLA、DSA)有本质差异:它带有按方法区分的稀疏 / indexer 元数据,以及打包的 FP8/BF16 KV 缓存布局(packed FP8 nope + BF16 rope),因此无法被并入 dense、MLA 或 DSA 的测试目录,而是单独放在test/registered/attention/unittests/dsv4/下。
该目录中唯一的注意力后端是dsv4,它通过flash_mla完成实际计算。能力矩阵中的每一行代表该后端在某个compress_ratio模式下的覆盖情况:
0:仅滑动窗口注意力(SWA-only);4:C4 稀疏压缩路径;128:C128 稀疏压缩路径。
矩阵的列是各类 runner 执行模式,覆盖 eager、CUDA graph 捕获/重放、EAGLE 投机解码等不同执行路径。相关实现位于 deepseek_v4_backend.py(CUDA 版本)与 deepseek_v4_backend_hip_radix.py(HIP/radix 变体),测试入口为 test_deepseek_v4.py。
覆盖率矩阵(Coverage Matrix)详解
矩阵以 runner 模式为列、compress_ratio模式为行。单元格使用以下符号语义:
- ✓ <variants>—— 该组合已被测试覆盖,单元格内列出具体的配置变体;
- ——— 不适用 / 未被测试;
- production-unreachable: <reason>—— 生产环境永远不会调用该组合,因此测试 runner 会在调用点(call site)直接断言其不可达;
- blocked: <reason>—— 一旦尝试就会触发硬断言崩溃,同样在调用点被断言;
- deferred: <reason>—— 未来可能落地,当前处于禁用状态。
完整覆盖矩阵
compress_ratio | Eager Phase 2 | CG decode | PCG extend | BCG extend | Verify eager | Verify CG | DE eager | DE CG | DE-V2 CG | EAGLE-draft runner | EAGLE-DE runner | FKVMTP runner |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
0(SWA-only) | ✓ EXTEND no-prefix / prefix-within-window / nonzeroattn_sink/ above-window / seq_len==SWA_WINDOW / seq_len below-page / seq_len at-page / seq_len above-page / prefix-exact-page / total-exact-page + DECODE within-window / multi-request / above-window | ✓ DECODE within-window + multi-request | — | — | ✓ EAGLE chain (topk=1)prefix_lens=(64,96) | ✓ EAGLE chain CGprefix_lens=(64,96) | ✓ EAGLE ragged-accept | ✓ EAGLE uniformextend_lens=(4,4) | — | ✓ chainprefix_lens=(32,64),num_steps=3(DeepseekV4MultiStepBackendcapture/replay vs. per-step-init eager) | ✓ uniformextend_lens=(4,4),prefix_lens=(64,96)(productionEAGLEDraftExtendCudaGraphRunnerthrough_create_dsv4_prefill_backend;uses looseDSV4_GRAPH_ATOL=1e-1and skips stricttopk_indexexact-match to absorb CG accumulation drift) | — |
4(C4) | ✓ EXTENDprefix_lens=(64,),extend_lens=(16,)+ DECODEprefix_lens=(64,)(extra K cache written directly viaset_extra_key_buffer;c4_sparse_page_indicesseeded manually because indexer is bypassed) | ✓ DECODEprefix_lens=(64,) | — | — | ✓ EAGLE chain (topk=1)prefix_lens=(64,96) | ✓ EAGLE chain CGprefix_lens=(64,96) | production-unreachable: draft layer is SWA-only | production-unreachable: draft layer is SWA-only | — | — | — | — |
128(C128) | ✓ EXTENDprefix_lens=(128,),extend_lens=(16,)+ DECODEprefix_lens=(128,) | ✓ DECODEprefix_lens=(128,) | — | — | ✓ EAGLE chain (topk=1)prefix_lens=(128,160) | ✓ EAGLE chain CGprefix_lens=(128,160) | production-unreachable: draft layer is SWA-only | production-unreachable: draft layer is SWA-only | — | — | — | — |
从矩阵中可以读出三条核心结论:
- SWA-only(
compress_ratio=0)是最完整的覆盖行,几乎所有 runner 模式都对其做了验证,包括 CUDA graph 捕获/重放、EAGLE draft / DE runner 等投机解码路径; - C4/C128 的覆盖集中在 eager / CG decode 与 verify 路径,因为压缩路径只在目标模型(target model)的 DECODE / TARGET_VERIFY 中使用;
- PCG extend、BCG extend 列全部为 "—",DSV4 的 split-op extend 在结构上不可达(详见下文"生产不支持的组合")。
矩阵在测试代码中的落地
矩阵中的每一列都能在 test_deepseek_v4.py 中找到对应的测试类与用例组:
- Eager 覆盖:
TestDSV4AttentionBackendCorrectness中的test_swa_only_cases(make_dsv4_cases("dsv4")生成的 SWA 全变体)、test_compress_attention_cases(C4/C128 的 dense extend 路径)与test_compress_attention_cases_sparse_prefill(C4/C128 的_forward_prefill_sparse稀疏 prefill 路径); - CG decode:
CUDA_GRAPH_DECODE_CASES中包含 SWA decode within-window、SWA multi-request(prefix_lens=(32, 96))、C4 decode(prefix_lens=(64,))与 C128 decode(prefix_lens=(128,))四类用例,由test_runner_mode_cuda_graph_decode_cases驱动; - Verify eager / Verify CG:
TARGET_VERIFY_CASES(SWA/C4/C128 各一例,prefix_lens分别为(64,96)、(64,96)、(128,160),extend_lens=(3,3))与EAGLE_VERIFY_CUDA_GRAPH_CASES(对应三个压缩比下的 EAGLE verify CUDA graph 捕获/重放,run_dsv4_eagle_verify_cuda_graph_case); - EAGLE-draft runner:
PRODUCTION_EAGLE_DRAFT_RUNNER_CASES走生产级EAGLEDraftExtendCudaGraphRunner链路,prefix_lens=(32,64)、num_steps=3,通过DeepseekV4MultiStepBackend(每个 draft 步一个DeepseekV4AttnBackend)捕获固定 batch 并重放不同请求的元数据; - DE eager(ragged-accept):
test_eagle_draft_extend_without_cpu_seq_lens以force_gpu_only_seq_lens=True覆盖 EAGLE ragged-accept 的 GPU-only seq lens 路径。
这些用例统一使用DSV4_PAGE_SIZE常量(即page_size=256),并通过DSV4AttentionCase数据结构参数化backend、forward_mode、num_heads、page_size、prefix_lens、extend_lens与compress_ratio。测试工具链位于 dsv4_attention.py,其中run_dsv4_attention_case、run_dsv4_compress_attention_case、run_dsv4_draft_extend_attention_case、run_dsv4_target_verify_attention_case分别对应矩阵中的不同列。
输入与配置覆盖(Input And Config Coverage)
DSV4 测试用例的输入与配置并非随意选取,而是严格对齐生产配置与底层 kernel 约束:
num_heads=64:与 DSV4 生产配置一致;flash_mla.sparse_decode_fwd对h_q有硬性取值约束(如 16/32/64/128),64 落在合法集合内;- DeepSeek-V4 形状元数据:
qk_nope_head_dim=448、qk_rope_head_dim=64、kv_lora_rank=448、head_dim=512(nope 与 rope 维度之和); page_size=256:这是后端硬编码并强断言的页大小——deepseek_v4_backend.py(README 标注的:355)、HIP radix 变体(:349)与 metadata.py(:134)都要求page_size == 256。因此按页边界设计的序列长度变体全部基于 256 展开:seq_len=255(差一页)、seq_len=256(恰好一页)、seq_len=257(超一页)、prefix_lens=256+extend_lens=4(前缀恰好占满一页)、prefix_lens=240+extend_lens=16(前缀+扩展恰好一页);seq_len=128则覆盖 SWA 窗口边界seq_len == SWA_WINDOW的情况。fixture 会自动放大这些大序列的max_context_len,确保req_to_token有足够空间;- 打包的 FP8 nope + BF16 rope SWA 缓存布局(584 bytes/token):来自
DeepSeekV4TokenToKVPool(实现位于 deepseek_v4_memory_pool.py),测试直接使用生产级缓存池; - SWA 窗口 = 128:
SWA_WINDOW常量定义在 deepseek_v4_backend.py; - 容差保持宽松:
DSV4_ATOL = DSV4_RTOL = 5e-2,用于吸收flash_mla在 FP8 GEMM 累加时相对反量化参考的数值波动;graph-replay 场景进一步放宽到DSV4_GRAPH_ATOL = 1e-1,以吸收use_prefill_cuda_graph=True引入的 padding 累加漂移。
值得注意的细节是:测试中max_context_len并非固定值,而是由 fixture 依据用例序列长度自动伸缩,这与生产调度器中req_to_token随上下文增长而扩容的行为一致(可对照后端中 apply_cp_reindex 对 token 计数与 CP round-robin 整除性的断言逻辑,理解 token 数在注意力后端中的敏感地位)。
参考实现原理(Reference Implementation Notes)
矩阵的每一格都依赖一个与生产路径相互独立的数值参考,这是本测试体系可信度的基石。
为什么参考实现不读取生产缓存
参考实现是纯 PyTorch 的 vanilla softmax,作用在被 fixture 保存下来的投影 BF16 K 上:
- 对 SWA 路径,K 来自
fixture._swa_bf16_k_per_req; - 对 C4/C128 路径,额外 K 来自
fixture._extra_bf16_k。
它刻意不去读回生产缓存中的字节。原因是:一旦参考实现直接反量化生产缓存的 FP8 字节,就会与quant_to_nope_fp8_rope_bf16_pack_triton/set_swa_key_buffer_radix耦合——如果 pack/write 环节存在静默 bug,生产路径与参考路径会以完全相同的方式损坏,测试将失去检测能力。参考实现的 vanilla BF16 K 与flash_mla实际读取的 FP8 反量化 K 之间存在 FP8 量化噪声,这一差异由DSV4_ATOL = DSV4_RTOL = 5e-2容差吸收。
C4/C128 参考如何获取注意力索引
对于 C4/C128,参考实现读取升级版DSV4AttnMetadata中每个 q token 的swa_page_indices/c4_sparse_page_indices/c128_page_indices,从而得知 kernel 实际会 attend 到哪些条目。由于投机图 runner 会在init_forward_metadata*之前调用expected_output,fixture 会在每次调用时重建当前 batch 的元数据,并在on_after_cuda_graph_warmup之后重新播种c4_sparse_page_indices,确保参考观察到与后端 forward 完全一致的索引集合。这一"重建-重播种"节奏对应 dsv4_attention.py 中expected_dsv4_output_from_inputs与_pure_torch_dsv4_combined_reference的实现逻辑。
attention-sink 修正的验证
注意力沉没(attention sink)修正是通过追加一个虚拟 key 实现的:该虚拟 key 带 per-head 分数attn_sink、value 为 0。默认attn_sink_value=-1e30时这在数值上是 no-op;而dsv4_swa_extend_nonzero_attn_sink用例以attn_sink_value=0.0实际触发并验证该修正逻辑。换言之,矩阵中"nonzeroattn_sink"变体专门用于证明 sink 修正不会破坏注意力数值。
生产不支持的组合(Production-Unsupported)
README 花费大量篇幅澄清:矩阵中的 "—" 与 "production-unreachable" 不是缺陷,而是后端断言体系对生产约束的显式编码。这些约束逐一列在下方,全部可以在后端源码中找到对应断言。
1.compress_ratio ∈ {4, 128}+DRAFT_EXTEND:生产不可达
DSV4 的 draft 模型(deepseek_v4_nextn.py 中的DeepseekV4ModelNextN)是单一 decoder 层,且以compress_ratio_override=COMPRESS_RATIO_NEXTN_LAYER = 0构建(该常量定义在:37,覆盖参数传入:97)。该值经由MQALayer.__init__(deepseek_v4.py 中compress_ratio_override参数的传递链)强制 draft 层无论config.compress_ratios如何都只走 SWA-only。因此:
- 生产环境永远不会调用
forward(compress_ratio=4 或 128, forward_mode=DRAFT_EXTEND); - 目标模型只在 DECODE / TARGET_VERIFY 路径使用 C4/C128(此时通过
need_compress=True填充 C4/C128 元数据); - 如果测试强行尝试该组合,
init_forward_metadata_draft_extend(deepseek_v4_backend.py 中 README 标注的:636-663)硬编码need_compress=False,c4_sparse_page_indices/c128_flashmla_metadata会保持为None——forward(compress_ratio=4)会触发extra_indices.shape[-1]相关错误,forward(compress_ratio=128)则会触发 flash_mla 的tile_scheduler_metadata断言。
测试 runner 在调用点做了双重保险:run_dsv4_draft_extend_attention_case与run_dsv4_eagle_draft_extend_cuda_graph_case都会断言case.compress_ratio == 0,把这种不可达状态在测试层面显式暴露出来。
2. MTPtopk > 1:树式投机结构性不可能
deepseek_v4_backend.py(README 标注的:369)断言self.topk in [0, 1],HIP radix 变体(deepseek_v4_backend_hip_radix.py 的:363)同样如此。DSV4 的投机 draft-extend / target-verify永远是链式(chain,topk=1),树式投机在结构上不可能。这正是矩阵中DE-V2 CG、EAGLE-draft tree runner、EAGLE-DE tree runner、FKVMTP runner列标 "—" 而非 "deferred" 的原因——这些 runner 本质依赖树式(tree spec)结构。
3. 非 256 的 page_size
page_size == 256的硬断言存在于 deepseek_v4_backend.py(:355)、HIP radix 变体(:349)与 metadata.py(:134)。测试侧通过DSV4_PAGE_SIZE = 256常量与全部用例的page_size=DSV4_PAGE_SIZE保持一致。
4. 非 512 的 head_dim
deepseek_v4_backend.py(README 标注的:345-347)断言head_dim == 512。DSV4 被硬编码为qk_nope=448 + qk_rope=64的组合,任何其他 head_dim 都无法通过。
5. 未知的compress_ratio
DSV4AttnMetadata.get_flashmla_metadata只接受Literal[0, 4, 128],超出范围会抛出ValueError(f"invalid {compress_ratio=}")(见 deepseek_v4_backend.py)。这一Literal类型与运行时校验的双重约束,是矩阵仅有三行的根本原因。
6._GraphBucket之外的 forward 模式
deepseek_v4_backend.py(README 标注的:320-328)对不属于{decode_or_idle, target_verify, draft_extend(v1 or v2)}的 forward 模式抛出NotImplementedError,init_forward_metadata(:713-714)同理。这意味着PCG/BCG 的 split-op extend 在结构上不可达——即矩阵中 PCG extend / BCG extend 两列全部为 "—" 的原因。
Compressor / C4Indexer:有意排除在矩阵之外
这是本 README 中一个容易被误读的边界,值得单独展开:Compressor与C4Indexer不是注意力后端的组成部分,而是 DSV4 模型(model)拥有的nn.Module实例。从 deepseek_v4.py 的源码结构看(self.compressor = None/self.indexer = None的初始化及后续按配置实例化的逻辑,位于:942-963附近),模型 forward 在注意力之前先调用self.indexer(...),再调用attn_backend.forward_core_compressor(x, ..., self.compressor)(如:1138-1160所示)。
流入注意力后端的只有两类输出:
- Compressor:把字节写入
extra_k_cache中由c4_out_loc/c128_out_loc指定的位置——而这些位置来自后端init_forward_metadata阶段的 Triton kernel(README 标注的deepseek_v4_backend.py:182),并非来自 Compressor 本身; - C4Indexer:写入
c4_sparse_page_indices字段,由后端的forward_extend/forward_decode读取。
因此后端与两者的契约可以概括为一句"黑盒读写":"我给你一个写入位置;你在那里写入了内容;我读取你写的内容。"当前 fixture 正是通过生产级 pack + store 路径(quant_to_nope_fp8_rope_bf16_pack_triton+set_extra_key_buffer,见 dsv4_attention.py 中_populate_extra_kv_cache的实现)供应已知正确的合成字节与索引,并将未量化的 BF16 K 暂存在 fixture 上供参考使用,从而精确验证这一契约。init_compression_metadataTriton kernel 本身被完整测试;被跳过的只是 Compressor 与 C4Indexer 的nn.Module前向数学(x → compressed_kv与x, q_lora → page_indices)。
Compressor / C4Indexer 的数学正确性属于组件级测试范畴。README 明确建议这类测试的天然归属是test/srt/下的组件单元测试(针对这两个模块数学的纯 PyTorch 参考),并指出这与 PLAN.md 中把 RoPE 排除在注意力后端矩阵之外的逻辑一致——预处理模块的输出是注意力后端的输入,其自身正确性不属于后端矩阵的职责。
测试中的两个专项契约类
除矩阵覆盖外,test_deepseek_v4.py 还包含两个专项测试类,值得在阅读矩阵时一并了解:
TestDSV4BreakableCudaGraphMetadataContract(CPU-only):验证 BCG(breakable CUDA graph)元数据重放契约。包括:cuda_graph_backend_decode/cuda_graph_backend_prefill默认不选 breakable(需要显式 CLI 标志才 opt-in);DSV4Metadata的快照(snapshot)边界声明(SharedReadEnds.PRE_REPLAY);稀疏 prefill 快照仅在num_qo_tokens超过_LARGE_INDEXER_QUERY_THRESHOLD时构建 chunk cache;refresh_for_breakable_cuda_graph_replay_对 tensor 字段采用值拷贝、对索引/元数据字段采用引用赋值,以在重放时保留捕获期的 tensor storage;以及SparsePrefillWorkspace的复用与扩容语义(复用不换地址、扩容换地址)。TestDSV4SwaOutCacheLocResolution:验证get_swa_out_cache_loc的"缓存快路径 vs 存储时回退"逻辑——仅当 per-forward 缓存值可证明是当前值时使用,否则回退到翻译out_cache_loc;同时覆盖 DP padding 导致out_cache_loc重绑、以及 IDLE 模式绝不复用陈旧缓存值(否则会把哑 token 写进活 KV 造成损坏)。
下一步工作(Next Work)
README 明确列出的后续工作是:在test/srt/下补上Compressor / C4Indexer 组件级正确性测试(独立于本矩阵)。该工作被标记为"可选"(optional)——因为注意力后端已经通过已知正确的合成输入验证了自身一侧的契约,组件数学的验证属于独立赛道。
如何在仓库中定位与本矩阵相关的代码
| 关注点 | 仓库相对路径 |
|---|---|
| 能力矩阵文档 | test/registered/attention/unittests/dsv4/README.md |
| 矩阵测试主体 | test/registered/attention/unittests/dsv4/test_deepseek_v4.py |
| 测试工具链(runner / 参考实现 / 缓存填充) | python/sglang/test/kits/attention_unittest/attention_methods/dsv4_attention.py |
| CUDA 注意力后端(SWA_WINDOW、断言、元数据) | python/sglang/srt/layers/attention/deepseek_v4_backend.py |
| HIP radix 注意力后端变体 | python/sglang/srt/layers/attention/deepseek_v4_backend_hip_radix.py |
| DSV4 注意力元数据(Literal 校验、PagedIndexerMetadata) | python/sglang/srt/layers/attention/dsv4/metadata.py |
| DSV4 模型(Compressor / C4Indexer 所有权) | python/sglang/srt/models/deepseek_v4.py |
DSV4 draft 模型(强制 SWA-only 的COMPRESS_RATIO_NEXTN_LAYER) | python/sglang/srt/models/deepseek_v4_nextn.py |
| 打包 FP8/BF16 KV 缓存池 | python/sglang/srt/mem_cache/deepseek_v4_memory_pool.py |
运行本矩阵测试的前提是 CUDA 环境且安装了带flash_mla模块的sgl_kernel——测试类通过_FLASH_MLA_AVAILABLE(importlib.util.find_spec("sgl_kernel.flash_mla"))与torch.cuda.is_available()双重守卫,缺失任一条件都会自动跳过(unittest.skipIf)。测试在 CI 中注册于base-bstage(4-GPU B200 与 1-GPU large 两类 runner 配置),分别预计耗时 14s 与 13s,可作为复现时估算资源占用的参考。
【免费下载链接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.项目地址: https://gitcode.com/GitHub_Trending/sg/sglang
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考