common: force draft model to SPLIT_MODE_NONE when target uses SPLIT_MODE_TENSOR - #2
common: force draft model to SPLIT_MODE_NONE when target uses SPLIT_MODE_TENSOR#2sunagent wants to merge 2 commits into
Conversation
…ODE_TENSOR DFlash/DSpark drafters share tensors with the target model (tok_embd, output). Under --split-mode tensor those tensors are split along axis 0, and the per-row ops in the draft graph (GET_ROWS, TOP_K, candidate selection) cannot consume an axis-0-split tensor, aborting with GGML_ASSERT(src_ss[0].axis != GGML_BACKEND_SPLIT_AXIS_0). Force the draft context to a single-device (non-split) scheduler so the shared tensors are copied into the draft's own buffers instead of being split. Tested on 2x RTX 5060 Ti 16GB (tensor split): Qwen3.8-27B NVFP4 + Qwen3.8-27B-DFlash2-Q4_K_M drafter now starts and sustains 51.6-58 t/s with 86-90% draft acceptance (n_max=2, 150k ctx, q4_0 KV).
DFlash/DSpark candidate selectors run per-row ops (TOP_K, GET_ROWS) on the shared target logits and cannot consume an axis-0-split (AXIS_1 vocab-split) result. Mirror output.weight onto every device when those spec types are enabled. For MTP and plain decoding the split is kept, saving ~1.2 GB VRAM per GPU (output.weight is ~2.4 GB; split = half per device). Verified: Qwen3.8-27B NVFP4 + DFlash2 on 2x RTX 5060 Ti tensor split - 51.6-58 t/s sustained, 86-90% draft acceptance; MTP mode unaffected (14.78 GB/GPU at 200k ctx, same as before this option existed).
|
I implemented an independent fix for the same crash — materializing draft-local copies of the shared
The two approaches are performance-equivalent within noise, so consider this a confirmation of the approach rather than a competing claim. Three suggestions if you keep iterating on this PR:
One more data point from the same runs: draft-side acceptance is heavily workload-dependent — 0.83 on code-gen vs 0.62 on prose with the identical setup. That explains most of the spread in the full matrix between workloads, and it's worth noting in the PR description if you cite the acceptance numbers. |
|
I would rather like to see split mode layer than none. @sunagent would split model layer work on your setup? |
Summary
Two commits that together make DFlash/DSpark work under
--split-mode tensoron multi-GPU setups. This is the complete fix chain (both are needed; plus upstream PR ggml-org#26636 for the shared-tensor device backends).Commit 1: draft model forced to SPLIT_MODE_NONE
common_base_params_to_speculative()(common/speculative.cpp) - when the target uses tensor split, the draft context no longer inherits it. DFlash/DSpark drafters share tensors with the target (tok_embd,output.weight) which are split along axis 0 under tensor parallelism; per-row ops (GET_ROWS,TOP_K, candidate selection) in the draft graph cannot consume axis-0-split inputs:GGML_ASSERT(src_ss[0].axis != GGML_BACKEND_SPLIT_AXIS_0) failed ggml-backend-meta.cpp:543Commit 2: output.weight MIRRORED only for DFlash/DSpark
llama-model.cpp+llama.h+llama-model.h+common.cpp- the target'soutput.weight(~2.4 GB) stays unsplit (MIRRORED on every device) so the draft's per-row logits ops see the full vocabulary. The option is wired to the enabled speculative types: mirrored only for DFlash/DSpark, split as before for MTP / plain decoding (saving ~1.2 GB VRAM per GPU vs always-mirrored).Why both
output.weightis vocab-split, logits are partial, and the DFlash selector cannot pick global top-k.add backends of the other model to the context) is required for the shared tensors to be reachable on all devices - complementary, not replaced by these commits.Tested
Hardware: 2x RTX 5060 Ti 16GB (sm_120, no NVLink), Ryzen 5 5600
Model: Qwen3.8-27B NVFP4 (tensor split) + incoai
Qwen3.8-27B-DFlash2-Q4_K_Mdrafter