[1/3] feat(dsv4): add tensor-parallel DeepSeek-V4 runtime - #70
[1/3] feat(dsv4): add tensor-parallel DeepSeek-V4 runtime#70calvarado2004 wants to merge 1 commit into
Conversation
Shard MLA heads, vocabulary, shared experts, and routed FP4 expert banks across TP ranks. Give every shard independent storage, size collectives for skewed expert loading, partition CPU workers, and avoid redundant checkpoint reads. This is the foundation that makes DeepSeek-V4-Flash fit and start reliably on 4x RTX A4000.
|
Benchmarked this on 2× RTX 6000 Ada (48 GiB each, PCIe Gen4 x16, no NVLink), 2× Xeon Gold 6526Y, 503 GiB host, serving Before this PR, TP replicated the expert banks per rank, so TP=2 was a regression:
The cache more than doubles and host memory nearly halves, which is the mechanism you describe — "the host expert banks divide by N instead of being replicated" — landing precisely as claimed. Residency goes from 23% to 51% of the pool, and the second PCIe link starts carrying half the gather instead of a duplicate of it. Under load it holds up: 8 concurrent streams give 94.0 tok/s aggregate against 66.5 at TP=1 (1.41×). It also beats the workaround I had been using — two independent single-GPU servers behind a load balancer — which managed 66.9 aggregate while consuming 2× host RAM. This PR obsoletes that recipe on every axis. I had independently written up the same design as an issue (#62) before finding this PR, having measured the replication problem and traced it to Test suite on the merged tree: 1606 passed, 10 skipped. The two failures are unrelated to this PR ( Two notes rather than objections:
Strong +1 from me. This is the change that makes a second GPU worth having for offloaded MoE. |
|
Thank you for the unusually thorough independent validation. The 2.17x single-stream gain, roughly 1.41x aggregate gain (94 versus 66.5 tok/s), larger KV cache, and lower host-RAM use on a 2x RTX 6000 Ada / 2-socket Xeon system validate both the TP execution path and the memory-sharding mechanism on hardware very different from the 4x A4000 development box. The full test result is also very helpful. Both operational issues are actionable:
I will keep these as focused operational follow-ups rather than mixing them into the TP math/data-path changes in this PR. Thanks again for testing scaling, concurrency, memory behavior, and the failure modes—not just the happy-path token rate. This is strong confirmation that the TP milestone is real. |
Implement the deployment feedback from the TP community testing: discover either an unversioned NCCL development link or a versioned runtime bundled with PyTorch, and fail with the searched paths before spawning ranks when neither exists. Expose a dedicated distributed/rendezvous port while preserving the existing API-port-plus-one default. Wire the DeepSeek-V4 test harness to the explicit port it already checks, so its collision guard and the engine use the same endpoint. Follow-up-to: FlashML-org#70 Builds-on: FlashML-org#90
|
Follow-up from the DSpark TP>1 hybrid CPU+GPU integration in #71. The TP sharding work here remains the foundation of our approach, and the newest measurements strengthen that decision. On the 4-GPU, dual-NUMA workstation we now emit a placement/accounting summary immediately before API readiness:
One nuance: this is logical ownership/accounting plus CPU affinity. Physical page residency still depends on allocator first-touch unless explicit mbind is added; we are keeping that distinction visible rather than overstating the NUMA guarantee. We also replaced the previous symmetric TP collective in this path with direct standard NCCL. In a comparable 87-call profile, NCCL CUDA time dropped from 878.0 ms / 92.1% to 27.2 ms / 15.8%. The bottleneck is now repeated expert-cache copying (65.9 ms / 38.3%), which is a much healthier place to tune the DSpark paper's hybrid CPU/GPU balance. Quality/performance remained stable: ~29.6 tok/s sustained on long coding, 33.07 tok/s observed peak on the repeated-route-aware experiment, clean 3,000-token code generation, valid required tool-call plus follow-up, and no repeated-token regression. Explicit reasoning_effort=high coding averaged 30.36 tok/s after warm-up with ~86.9% isolated acceptance. Our recommendation is therefore to keep this TP-aware sharding design and build adaptive DSpark verification on top, while treating explicit NUMA page binding as a separately measurable follow-up rather than conflating it with rank/core affinity. Detailed integration measurements: #71 (comment) |
Outcome
DeepSeek-V4-Flash can now run as a real tensor-parallel model in FreeToken instead of replicating model and expert storage on every rank. This is the memory and execution foundation required by the DSpark work in PRs 2 and 3.
On the validation system, the standalone milestone loads and serves
DeepSeek-V4-Flash-0731with TP=4 across four 16 GiB RTX A4000 GPUs while keeping the routed expert banks in host memory.Stack and review boundary
This is PR 1 of 3:
pr/dsv4-tp-runtime7f0d8fcpr/dsv4-dspark-exact423edd1..d42be9bpr/dsv4-dspark-adaptive13d4560..38d7b2fThis PR is intentionally independent of speculative decoding: 13 files, 680 additions, 95 deletions relative to
main. Reviewing it first keeps checkpoint ownership and collective placement separate from the much larger draft/verify state machine.Why this is needed
The original DSV4 runtime had single-rank model shapes and checkpoint-loading assumptions. Merely launching multiple ranks would still replicate the largest tensors and could produce mathematically incomplete rank-local MoE outputs.
The implementation therefore defines an explicit ownership rule for every DSV4 tensor, validates that rule before model construction, loads only the rank-owned checkpoint slices, and places collectives at the points where rank-local partials become complete model values.
The goal is not generic “split every large tensor” TP. DSV4's latent-KV attention, Lightning Indexer, recurrent compressors, routed experts, and quantization metadata have different ownership requirements. Those requirements are preserved rather than reshaped to make a requested TP size fit.
Tensor ownership and collectives
[rows, vocab]wq_bwo_ao_groups; partial groups are rejected at configuration timewo_bmoe_inter_dimmoe_inter_dimsplitThe shared and routed MoE branches deliberately do not reduce independently. They are partial sums over the same output dimension, so they are added first and all-reduced once. Reducing each branch separately would add a collective per layer without changing the result.
Checkpoint loading and memory correctness
(rank, world_size), exact division, rank slicing, and up-front model validation.o_groups, attention heads, vocabulary, and the quantized MoE intermediate dimension. Incompatible sizes fail with a targeted configuration error before any layer is built.narrow()can still be a view; retaining it kept the complete parent allocation alive and was measured to cost 5.1 GiB per GPU on this checkpoint at TP=4.For the validation checkpoint, TP=4 distributes the routed host banks across ranks instead of holding four full copies; the ranks retain approximately the same aggregate expert data that a single-rank run requires.
CPU-offload integration
The CPU MoE executor now follows the same rank-local expert layout as the GPU path. It also partitions the process's available physical cores across TP ranks, including affinity handling, so four ranks do not each create a full-machine worker pool and oversubscribe the host.
This matters for the later hybrid path: GPU cache hits/fetches and CPU overflow computation must produce compatible rank-local partials before the TP reduction.
Suggested review order
deepseek_v4/parallel.py,args.pymodel.py,attention.py,moe.pyweight.pymoe/cpu_executor.pyengine/config.py,engine/engine.py,server/args.py, docstests/dsv4/test_dsv4_tensor_parallel.pyValidation
Focused coverage includes:
moe_inter_dimwith no gaps or overlap;The completed three-PR stack passes the full DSV4 suite on the target server:
Runtime milestone:
Scope and non-goals
mtp.*drafter.The next PR uses these ownership and collective contracts to add exact DSpark decoding without changing the TP model math established here.