Repository navigation
[Model] DeepSeek-V4.1-Flash on 4x V100: 512K packed KV, faster prefill, API launch - #1277
Merged
Merged
Conversation
…l, API launch Runs DeepSeek-V4.1-Flash-1017 (GSQ-RCO IQ2_XXS GGUF + Engram IQ4_NL + DSpark5 draft) on 4x V100-32G (TP4, one x16 PCIe uplink, 62 GB host RAM) with expert offloading. All new paths are lossless and switchable. Prefill (G1-G7): - G1 activation memory: per-tile MoE scratch + FP32 accumulator, in-place expand_hc / large all-reduce, windowed hc_transition, pynccl all_gather, empty_cache before big passes; chunks up to 16K. - G2 cold-expert staging ring issued ahead across tiles and layers. - G3 routed down projection split by output rows (no duplicated TP boundary blocks, -2.25 GiB/GPU) with an IPC peer gather for decode. - G5 Engram readers, adaptive indexer tile, persistent activation grid. - G6 routed-only staging for short prompts. - G7 same-session prefix cache (scheduler guard + worker restore_prefix). Long context (G4): - Window layers keep 256-row private rings. - Packed KV (VLLM_V41_PACKED_KV): FP4 + E4M3 compressed latents in a shared host memfd plus a 32K GPU hot ring, FP4 index keys on GPU, CUDA decode index scores and segment-parallel exact top-k. Enables 512K. - RoPE rotates on the fly past its table; C2/Engram/guard limits and the DSpark draft length cap are lifted to the target length. Results vs phase 1: TTFT short 5.6 -> 0.8-1.1 s, 8K 27.7 -> 5.8 s, 15K 44.0 -> 11.1 s; 128K 110 s and 500K 651 s (needle passes); follow-up turns hit the prefix cache in ~1-1.7 s; fresh-text decode 57.8 -> 53.5 ms/round. Tokens at <=16K are identical with packed KV. Tooling: benchmarks/deepseek_v41/ gains the v41k SM70 extension source and build script, serve configs (phase 2: 16K, phase 3: 512K + prefix cache + tools + thinking), launch/validation tools, GPU micro-tests and HANDOFF.md. V4.1 switches are registered in vllm/envs.py and read through vllm.envs. Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Apply the CI hook output for the new csrc/v41k sources and HANDOFF.md, rename kend -> k_end, type the V4.1 env switches that have defaults as str and add annotations / targeted type-ignore codes for the remaining mypy findings. No runtime change. Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
clang-format 21 is not idempotent on the brace-less `if (...) MACRO(...) else ...` launch chains; give them explicit braces (same expansion) and fence them with clang-format off/on. Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: yangzhuxinyzx <153831768+yangzhuxinyzx@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yangzhuxinyzx
added a commit
that referenced
this pull request
Oct 28, 2026
* [Bugfix] Keep the pynccl all-reduce out-of-place by default #1277 made pynccl reduce every all-reduce of 4194304 or more elements in place. The vllm.all_reduce op is registered as out-of-place, so compiled graphs may read its input again after the call; reducing in place hands those readers the summed tensor instead of the rank's own partial. Qwen3.8-27B NVFP4 + DFlash2 on TP4 V100 crosses the threshold on every prefill of about 820 tokens or more. Greedy target output is unchanged (identical tokens on all eight 1K/8K fixtures, identical logprobs at 1K), but the draft context changes: speed-fixture tokens per round fell from 2.875/2.974 to 2.655/2.613 at 1K/8K. Unset VLLM_INPLACE_AR_MIN now means out-of-place, except with VLLM_V41_PACKED_KV=1, where the DeepSeek-V4.1 packed-KV prefill keeps the in-place reduction it was qualified with. An explicit value still wins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * [Bugfix] Resolve the in-place all-reduce default in envs.py Keep the communicator free of env and model coupling (check-layering): envs.py now resolves the default, so the communicator reads envs.VLLM_INPLACE_AR_MIN. Unset still means out-of-place, except with VLLM_V41_PACKED_KV=1. Also drop the model names that #1265 added to two comments in SpeculativeConfig, which pushed its model coupling over the baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * [Doc] Regenerate the architecture runtime reference #1265 added five lines to vllm/config/speculative.py, which moved the SpeculativeConfig line anchors in runtime_reference.md and left check-architecture-reference failing on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Peuqui
added a commit
to Peuqui/1Cat-vLLM
that referenced
this pull request
Oct 28, 2026
…ather The batched-matmul sparse MLA attention ran six elementwise torch ops around its two BLAS calls: a broadcast masked_fill over the gathered keys, then scale, copy into the sink-padded logits, mask, sink column, softmax and the fp16 cast of the probabilities. On a 128-token pass with 64 heads and 640 keys they took 820 of 1434 us on RTX 8000 in the per-stage nsys profile of the DSv4 PP5 entry (150k-token prefill); the masked_fill alone, a non-vectorized broadcast kernel, took 395 us, twice the gather. Now the gather writes zeros for unused slots and never reads their indices (a Triton row gather for prefill; the decode dequantizing gather takes the same mask), and one Triton kernel reads the fp32 scores once and writes fp16 probabilities: scale, mask, sink column and the fp32 softmax with libdevice exp, the same expf torch's softmax uses. The fp32 logits and probs buffers go away (140 -> 110 MiB per 128-token pass at 640 keys). DeepSeek-V4.1 (1CatAI#1277) calls attend_gathered_keys as well. Its two callers in vllm/models/deepseek_v41/attention_ops.py now zero the unused slots themselves, since their gathers leave them as read, and pass the probabilities as [T, H, W] in the key dtype. Against main's function on random data with NaN in the unused slots, 99.69-100 % of their outputs are bit-identical and the rest differ by one fp16 step (at most 2.4e-4), on a Tesla V100 and a Quadro RTX 8000. Old against new in CUDA graphs (us per call, 10 calls per graph): prefill pass T128 H64 W640: RTX 8000 1438.3 -> 755.7, V100 1178.7 -> 632.0 prefill pass T128 H64 W2176: RTX 8000 4666.8 -> 2367.7, V100 3760.3 -> 1983.5 decode T6 H64 C4 (128 + 512 keys): V100 146.5 -> 96.5 decode T6 H64 SWA only (128 keys): V100 55.1 -> 36.6 Error against a float64 reference is unchanged (max and mean equal to three digits); 99.2-99.8 % of the outputs are bit-identical, the rest differ by at most 3e-5 (softmax reduction order). Tests: tests/kernels/attention/test_dsv4_sparse_prefill_bmm.py and test_dsv4_sm70_sparse_bmm.py 29 passed on V100; on RTX 8000 17 passed, 12 skipped (the decode reference kernel is Volta-only); pre-commit and mypy-3.10 passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YaG8VjXn5eN3Aq7eZrA3dp Signed-off-by: Peuqui <43776522+Peuqui@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR runs DeepSeek-V4.1-Flash-1017 on 4× V100-32G (54633) with expert offloading:
The hardware is TP4 behind a single x16 PCIe uplink at 12.2 GB/s, with 62 GB host RAM.
It adds the phase 2–3 work: prefill goals G1–G7, 512K context through lossless packed FP4 KV kept in host memory, and an OpenAI API launch with prefix cache, tool calls and thinking. Every new path has its own switch. The defaults are lossless.
The full handoff is
benchmarks/deepseek_v41/HANDOFF.md. It covers launch, switches, results, dead ends and open items.Changes
G1: activation memory
expand_hcand large all-reduces run in place.hc_transitionis windowed.all_gathergoes through pynccl.empty_cacheruns before big passes.G2: cold-expert staging ring
G3: routed down projection split by output rows (
VLLM_V41_DOWN_ROWS)peer_gatherkernel in v41k.G4: long context
VLLM_V41_PACKED_KV):G5: prefill compute
G6: short prompts
G7: same-session prefix cache (
VLLM_V41_SESSION_PREFIX)restore_prefixrewinds the Engram and C2 state.Tooling (all under
benchmarks/deepseek_v41/)build.py.serve-ds41-phase{2,3}.yaml.tools/run_serve{2,3}.shand the validation tools.Configuration
vllm/envs.py. The generic modules read them throughvllm.envs, so the layering ratchet does not grow.deepseek_v41_activationsmoved into the model package.tools/config_boundaries.py.Launch
Results (vs phase 1)
With packed KV, tokens at ≤16K are identical to the paged FP16 cache.
Test plan
benchmarks/deepseek_v41/tests/: kv_pack, packed attention, packed indexer, parallel top-k, window ring, G3 down, peer gather, G1 memory, RoPE tail. Each is bit-exact or equivalent.check-layeringreportsmoe_runner.pyplatform coupling 37 → 39. This was already onmainbefore this PR.🤖 Generated with Claude Code