The Moment of Reckoning: Accepting the sm_120 Ceiling on DeepSeek-V4-Flash
In the long arc of a complex ML engineering session, there comes a moment when the numbers don't lie. After hours of environment setup, dependency resolution, model downloads, and careful launch orchestration, the assistant in this opencode session finally had a concrete number: 10.22 tokens per second at batch size 1. The model card for DeepSeek-V4-Flash advertised roughly 107 tok/s on vLLM with comparable hardware. The gap was an order of magnitude — and message 12380 captures the precise instant when the assistant confronted that gap, diagnosed its root cause, and made the strategic decision to accept the bottleneck and pivot to the project's headline deliverable.
This message is a study in engineering decision-making under hard constraints. It is not a triumphant breakthrough or a clever optimization. It is something rarer and arguably more valuable: the disciplined recognition that some problems cannot be solved within the available time and scope, and the wisdom to move forward anyway.
The Numbers That Changed Everything
The message opens with the assistant's reasoning block, which directly confronts the performance disaster revealed in the previous message ([msg 12379]):
The bs=1 throughput is drastically underperforming—I'm getting around 10 tok/s with a 94ms TPOT, which is roughly 10x slower than the model card's expected ~107 tok/s on vLLM. This gap is likely due to SGLang running on sm_120 with the marlin-to-sm120_triton MoE path, torch indexer logits forced on sm120, and without DeepGEMM or CUDA optimizations.
The tone is measured but the stakes are clear. The assistant had just successfully deployed DeepSeek-V4-Flash — a 284B-parameter Mixture-of-Experts model with 13B active parameters — on SGLang across 4× NVIDIA RTX PRO 6000 GPUs (sm_120 architecture). The model loaded, the CUDA graphs captured, and the warmup request returned "The capital of France is Paris." — correct output. But correctness without performance is not deployment; it is a proof of concept.
The assistant immediately begins constructing a causal model of the performance gap:
The slow paths are the culprit: the sm120_triton MXFP4 MoE kernel is unoptimized, the torch indexer logits fallback is expensive, TF32 might not be enabled, and the DSA indexer computation per layer adds overhead.
This is not guesswork. The assistant draws on deep knowledge of the SGLang codebase and the NVIDIA GPU architecture. The model uses FP4 (MXFP4) quantized experts, which on sm_100 (Blackwell B200) would route through DeepGEMM and NVIDIA's trtllm-gen compiled kernels — highly optimized tensor-core paths. On sm_120 (the RTX PRO 6000 Blackwell variant), those fast paths are arch-gated: the CUDA capability check is_sm100_supported() returns False, and SGLang falls back to Triton-based kernels that run on CUDA cores (SIMT) rather than tensor cores. The result is an order-of-magnitude throughput penalty baked into the software stack.
The Diagnostic: Ruling Out Misconfiguration
Before accepting the bottleneck, the assistant does what any good engineer would: it runs a targeted diagnostic to rule out configuration errors. The bash command in the message checks three things:
- Server health — is the SGLang server still alive after the benchmark?
- CUDA graph status — is the decode phase actually using CUDA graphs, or did graph capture fail silently?
- Straggler processes — is a previous benchmark still running and interfering? The results are unambiguous:
=== server alive? ===
healthy
=== cuda graph in startup log ===
[2026-06-17 15:36:30 TP0] Decode batch, #running-req: 8, #full token: 3072,
full token usage: 0.00, #swa token: 3072, swa token usage: 0.02,
cuda graph: True, gen throughput (token/s): 24.29, #queue-req: 0
The server is healthy. CUDA graphs are active (cuda graph: True). The decode batch log shows 24.29 tok/s at batch size 8 — confirming that even with CUDA graphs engaged, throughput is anemic. This is not a configuration issue; it is a fundamental performance ceiling.
The assistant's reasoning crystallizes this conclusion:
Diagnosis clear: CUDA graph IS active (cuda graph: True), but decode is genuinely slow — ~24 tok/s aggregate even at bs=8, ~11 tok/s at bs=1. This is the inherent sm_120 fallback cost (triton MXFP4 MoE + forced torch indexer-logits; all the SM100 fast paths are gated off). Not a misconfig — CUDA graphs on, GPUs compute-bound in the fallback kernels.
Decision-Making Under Constraints
The most interesting part of this message is the assistant's internal debate about what to do next. The reasoning block reveals a structured decision process:
- Identify quick wins: The assistant considers enabling TF32 precision (
torch.set_float32_matmul_precision('high')), checking for alternative MoE runner backends, and enabling MTP/EAGLE speculative decoding. Each is evaluated for potential impact. - Estimate the ceiling: The assistant recognizes that even with all quick wins, the fundamental problem remains — the sm_120 Triton fallback kernels are unoptimized by design. The fast fused DSA/MoE stack (DeepGEMM, trtllm-gen, FP4 indexer) is arch-gated to SM100. No amount of configuration tuning can close a 10× gap.
- Weigh against project scope: The assistant explicitly acknowledges the temptation to dive into kernel optimization:
That said, I'm getting sidetracked on performance tuning when the real deliverable is the PD disaggregation.
This is the critical moment. The project's headline deliverable is prefill-decode (PD) disaggregation — splitting the prefill and decode phases across separate GPU groups to improve throughput and latency. The assistant has been tasked with deploying DeepSeek-V4-Flash with PD disaggregation, benchmarking it, and tuning performance. The single-node TP4 baseline was supposed to be a quick validation step before the main work. Instead, it revealed a 10× performance gap that could consume days of kernel engineering.
The assistant makes a deliberate choice: record the baseline, accept the bottleneck, and proceed to PD disaggregation. This is not laziness or defeat — it is scope discipline. The assistant correctly recognizes that optimizing sm_120 kernels is a multi-week effort comparable to the earlier K2.6 custom kernel work (referenced in the segment context), and that effort belongs to a different project phase.
Assumptions and Blind Spots
Every engineering decision rests on assumptions, and this message is no exception. Several are worth examining.
Assumption 1: The sm_120 fallback path is inherently 10× slower. This turns out to be broadly correct but incomplete. Later in the session (chunk 1 of this segment), the assistant discovers that the MoE slot-GEMV and sparse-decode attention kernels consume 39% and 38% of GPU time respectively, both running on CUDA cores rather than tensor cores. The NVFP4 quantization switch (applied later) routes MoE through tensor-core paths and yields a ~24% improvement, confirming that the MoE portion was indeed bottlenecked by the fallback path. But the attention kernel remains the dominant bottleneck regardless of MoE backend.
Assumption 2: MTP speculative decoding won't close the gap. The assistant briefly considers MTP but dismisses it. Later (chunk 1), MTP actually delivers a 47% throughput improvement for single-request scenarios — significant, though not enough to close the 10× gap. The dismissal was reasonable given the scope, but it left a measurable improvement on the table.
Assumption 3: The 10× gap is "plausible" for sm_120 vs B200. The assistant writes: "Given the model is 284B/13B active in FP4, a 10x slowdown compared to a B200 with triton fallbacks and torch indexing is plausible." Later analysis reveals that the hardware should be capable of 300–600 tok/s at C=16 based on roofline analysis (1.9 TB/s VRAM bandwidth, 13B active parameters). The actual throughput of ~28 tok/s represents a 40× gap to the user's target of ~1000 tok/s, not 10×. The assistant's initial framing underestimated the severity of the bottleneck.
Assumption 4: PD disaggregation is the right next step. This assumption is validated by the session's later success — PD disaggregation is achieved and verified end-to-end. However, the assistant also discovers that PD disaggregation alone does not improve decode throughput (the bottleneck remains the same kernels regardless of which GPUs run them). The value of PD disaggregation is in resource isolation and prefill-throughput scaling, not in fixing the decode bottleneck.
The Broader Significance: The sm_120 Ceiling
Message 12380 is significant beyond its immediate context because it documents a structural limitation in the current ML inference ecosystem. The NVIDIA RTX PRO 6000 (sm_120) is a Blackwell-generation GPU, but it is not a B200. The sm_120 architecture lacks certain tensor-core capabilities that the B200's sm_100 provides, and the software stack — SGLang, vLLM, DeepGEMM — has optimized primarily for the sm_100 path. The sm_120 path is a fallback, maintained for correctness but not performance.
This creates a frustrating situation for engineers deploying on pro-grade Blackwell hardware: the model runs correctly, the CUDA graphs capture, the attention backend selects correctly, but the throughput is an order of magnitude below what the hardware's raw specifications suggest. The bottleneck is not in any single kernel but in the software architecture's assumption that sm_100 is the target. The sm_120 path is a compatibility layer, not a performance path.
The assistant's response — to document the bottleneck precisely, accept the constraint, and move forward — is the correct engineering response. But the message also implicitly raises a question that the session does not answer: who will optimize the sm_120 path? The answer, as the later chunk summary notes, is that it would require "a multi-week custom kernel effort akin to the earlier K2.6 work" — a significant investment that is outside the scope of this deployment session.
Conclusion: The Discipline of Acceptance
Message 12380 is not about a breakthrough. It is about the moment when an engineer looks at a 10× performance gap, runs the diagnostics, confirms the root cause, and decides — deliberately and with full awareness of the trade-offs — to accept the constraint and reorient toward the achievable deliverable.
This is a skill that is rarely taught but essential in production ML engineering: the ability to distinguish between a solvable problem and a fundamental constraint, and the discipline to act on that distinction. The assistant could have spent the rest of the session chasing kernel optimizations, trying alternative MoE backends, or debugging TF32 precision settings. Instead, it recorded the baseline, installed NIXL, and moved to PD disaggregation — the work that actually needed to get done.
The message ends with the assistant installing NIXL, the first step toward the PD disaggregation deployment. The 10 tok/s baseline is preserved in a benchmark file, a honest record of what the sm_120 fallback path delivers. The project continues, not because the bottleneck was solved, but because the engineer understood which battles to fight and which to accept.