[None][feat] Enable DeepSeek-V4 and DSA (DeepSeek-V3.2/GLM) serving on SM120 via FlashInfer sparse-MLA#16224
Conversation
…on SM120 SM120 compiles out the trtllm-gen sparse-MLA path in the C++ attention op for both phases (attentionOp.h mUseTllmGen excludes SM120), so DeepSeek-V4 could not serve on SM120/SM121. Route attention through flashinfer 0.6.14's footer-scale sparse-MLA kernels instead: - DeepseekV4FlashInferAttention reuses the TRTLLM backend's metadata, indexer, compressor, and local->global index conversion; one flashinfer call serves context and generation (decode fast path <= 64 query tokens, prefill orchestrator above). - SWA and COMPRESS pools switch to the kernels' footer-scale page layout (per token: 448 B fp8-e4m3 nope + 128 B bf16 rope + a UE8M0 tile-scale footer row); requires kv_cache_config.tokens_per_block=256 so compressed block sizes (64, 2) land on the instantiated extra page sizes. - The layout switch derives from model_config.attn_backend on both the cache-manager and backend sides; the fused C++ compressor scatter is replaced by an equivalent Python postprocess + Triton footer-scale scatter, and the SWA append (fused into mla_rope_generation on the C++ path) moves into the backend. RoPE runs in mla.py (support_fused_rope False); DSv4-specific fused-rope and fused-FP8-Q paths are gated on rope_fusion. - The local->global index kernel gains an optional split-segment output (combined table unchanged for the C++ consumer), removing two per-layer per-step slice copies (the flashinfer FFI reads indices as contiguous). - Decode split-K scratch is explicitly initialized (unwritten splits leak allocator garbage into the merge otherwise), and all pool byte offsets are computed in 64-bit (int32 slot-id byte products overflow past a 2 GiB pool, reached at ~1M-token contexts). All-Python change: no C++ rebuild required. Signed-off-by: Zihua Wu <13583761+lucifer1004@users.noreply.github.com>
… backend The FLASHINFER block-reuse workaround targets the dense flashinfer wrapper; DeepSeek-V4 routes attention through its dedicated sparse backend, which reads reused blocks through the same position-based block tables as fresh ones. Defer the workaround to after model-engine creation (the pattern the MambaHybrid disagg disable already uses, incl. the retro-flip of the engines' cache_reuse feature) and exempt deepseek_v4 sparse attention; dense-wrapper behavior is unchanged. Depends on the indexer K-cache stride fix (build_indexer_params deriving the compressed block stride live): with it, reuse-enabled DSv4 passes the full closed-loop qualification (random-8k1k concurrency ramp to c=32 that previously faulted in indexerKCacheGatherUnifiedKernel, gsm8k 0.9447) and a resume-semantics probe (early-fact via compressed KV/indexer and near-boundary fact via the SWA window both correct across a mid-block partial-reuse boundary, prefix hits confirmed by 1.89x TTFT). Signed-off-by: Zihua Wu <13583761+lucifer1004@users.noreply.github.com>
|
Caution Review failedAn error occurred during the review process. Please try again later. 📝 WalkthroughWalkthroughDeepSeek-V4 adds a FlashInfer sparse MLA backend using footer-scale KV pages, Python compression scatter, split sparse indices, updated RoPE handling, backend-specific cache sizing, and executor routing. ChangesDeepSeek-V4 FlashInfer integration
Estimated code review effort: 5 (Critical) | ~120 minutes Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant Executor
participant DeepseekV4FlashInferAttention
participant CacheManager
participant Compressor
participant FlashInferMLA
Executor->>CacheManager: create footer-scale cache layout
Executor->>DeepseekV4FlashInferAttention: route DeepSeek-V4 attention
DeepseekV4FlashInferAttention->>Compressor: postprocess and scatter compressed KV
DeepseekV4FlashInferAttention->>CacheManager: append SWA KV rows
DeepseekV4FlashInferAttention->>FlashInferMLA: run paged sparse MLA attention
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
forward_dsa_attn's phase split pre-rotated q_pe/k_pe for both phases when a non-fused backend runs RoPE in Python (apply_rotary_emb), copying forward_impl_with_deepseek_v4's pattern. That pattern is only correct for DeepSeek-V4, whose forward_absorption_generation branch skips _mla_gen_rope; the generic branch rotates q_pe/k_pe itself, so the DSA generation phase rotated them twice — and _mla_gen_rope's position_ids re-slice by num_ctx_tokens additionally misaligns pre-sliced positions in mixed batches. The context phase had the opposite defect: the rotated k_pe never reached the latent cache, so a backend appending latent rows itself cached an unrotated rope half. Fix, scoped to the DSA phase split: the generation branch no longer pre-rotates and passes the full position_ids for _mla_gen_rope's own slicing; the context branch keeps its pre-rotation and syncs the latent cache's rope half, as forward_impl does for the dense path. The combination was unreachable before a non-fused backend existed for DSA (the fused TRTLLM backend rotates and appends in-kernel), and the shared absorption functions are untouched, so no other configuration changes behavior. Signed-off-by: Zihua Wu <13583761+lucifer1004@users.noreply.github.com>
SM120 compiles out the trtllm-gen sparse-MLA kernels in the C++ attention op (attentionOp.h excludes SM120 from mUseTllmGen), so DSA models (DeepSeek-V3.2, GLM glm_moe_dsa) cannot serve on SM120/SM121. This routes DSA attention through flashinfer 0.6.14's SM120 V32-family sparse-MLA kernels instead, following the DeepSeek-V4 FlashInfer backend's structure. All-Python change; no C++ rebuild required. - DSAFlashInferAttention reuses the TRTLLM DSA backend's metadata, indexer (including cross-layer sharing), and local-to-global index conversion; one flashinfer call serves context and generation (decode fast path <= 64 query tokens, prefill orchestrator above), with decode split-K scratch explicitly initialized. Backend selection rejects non-SM120/121 with a clear error. - The main latent pool switches to the kernels' inline-scale page layout (FlashMLA ABI: per token 512 B fp8-e4m3 nope in 4x128 tiles, 4 inline fp32 scales, 128 B bf16 rope; GLM-style arbitrary-fp32 scale mode), 64-token pages. The Triton writer mirrors SGLang's quantize_k_cache byte for byte. BF16- and FP8-declared KV checkpoints land in the same physical layout. - The append moves into the backend: write slots come from the same convert_req_index_to_global op the top-k conversion uses, fed each token's own absolute position (refreshed per step alongside the indexer slot mappings, overlap-scheduler-safe). - The softmax scale uses the per-head qk width from mla_params rather than the absorbed latent width the backend head_dim carries: they coincide for DSv4 (512) but differ for DSA geometries (GLM-5.2: 256 vs 576). - The FLASHINFER block-reuse workaround keeps prefix caching for the DSA sparse backend, same as DeepSeek-V4. Validated on 8x RTX PRO 6000 Blackwell (SM120), GLM-5.2-NVFP4 and GLM-5.1-NVFP4 at tp8/ep8: gsm8k exact_match 0.9568 (threshold 0.8), serving bench random-8k1k 289 output tok/s peak at concurrency 16 with 21.7 ms single-user ITL, kernel numerics vs a torch reference over the dequantized cache (min cos >= 0.998 across decode/prefill dispatch), packer byte-identical to SGLang's, and a 92-request tool-use campaign (2k-10.8k tokens, block reuse and partial reuse on) all clean. Signed-off-by: Zihua Wu <13583761+lucifer1004@users.noreply.github.com>
|
Scope update: pushed two additional commits extending this enablement to the DSA family (DeepSeek-V3.2 / GLM |
SM120 compiles out the trtllm-gen sparse-MLA path in the C++ attention op for both phases (
attentionOp.hexcludes SM120 frommUseTllmGen; generation asserts atattentionOp.cpp:3156), so neither DeepSeek-V4 nor DSA models (DeepSeek-V3.2, GLMglm_moe_dsa) can serve on SM120/SM121. This PR routes both families through flashinfer 0.6.14's SM120 sparse-MLA kernels (footer-scale cache for DSv4, inline-scale for DSA). All-Python change; no C++ rebuild required. Four commits: DSv4 backend, DSv4 reuse policy, DSA RoPE fix, DSA backend — each separable.DeepSeek-V4 (commits 1–2)
DeepseekV4FlashInferAttentionreuses the TRTLLM backend's metadata, indexer, compressor, and local-to-global index conversion; one flashinfer call serves context and generation (decode fast path ≤64 query tokens, prefill orchestrator above).kv_cache_config.tokens_per_block=256so compressed block sizes (64, 2) land on the instantiated extra page sizes.model_config.attn_backendon both the cache-manager and backend sides; the fused C++ compressor scatter is replaced by an equivalent Python postprocess + Triton footer-scale scatter; the SWA append moves into the backend; RoPE runs in mla.py (support_fused_ropeFalse), with DSv4's fused-rope and fused-FP8-Q paths gated onrope_fusion.DSA family — DeepSeek-V3.2 / GLM (commits 3–4)
DSAFlashInferAttentionreuses the TRTLLM DSA backend's metadata, indexer (including GLM-5.2's cross-layer top-k sharing), and local-to-global index conversion; one flashinfer call serves context and generation, with decode split-K scratch explicitly initialized. Backend selection rejects non-SM120/121 with a clear error.quantize_k_cache. BF16- and FP8-declared KV checkpoints land in the same physical layout.convert_req_index_to_globalop the top-k conversion uses, fed each token's own absolute position (refreshed per step alongside the indexer slot mappings, overlap-scheduler-safe).mla_paramsrather than the absorbed latent width the backendhead_dimcarries — they coincide for DSv4 (512) but differ for DSA geometries (GLM-5.2: 256 vs 576).forward_dsa_attn's phase split: the generation phase pre-rotated q_pe/k_pe thatforward_absorption_generationrotates again (the pattern is only correct for DeepSeek-V4, which skips_mla_gen_rope), and the context phase never propagated the rotated k_pe into the latent cache. Shared absorption functions and fused-RoPE backends are untouched.Validation (RTX PRO 6000 Blackwell, SM120; closed-loop serve+eval+bench with verified cleanup)
DeepSeek-V4 (4 GPUs):
DSA family, GLM-5.2-NVFP4 and GLM-5.1-NVFP4 (8 GPUs, tp8/ep8):
Depends at runtime on #16217 (DeepGEMM SM120 indexer kernels) and flashinfer ≥ 0.6.14; the reuse commit depends on #16223. Builds cleanly with #16188/#16215/#16216 for externally managed build environments.
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes