Skip to content

Fix mtp tp - #224

Closed
randoentity wants to merge 3 commits into
turboderp-org:masterfrom
randoentity:fix-mtp-tp
Closed

Fix mtp tp#224
randoentity wants to merge 3 commits into
turboderp-org:masterfrom
randoentity:fix-mtp-tp

Conversation

@randoentity

Copy link
Copy Markdown

Fixes MTP with TP and includes fix from @alexhunt7 (non-TP multi GPU).

AI use disclosure: the changes were made by LLM. I tested both TP and auto split paths successfully.

See theroyallab/tabbyAPI#424 (comment) for more details.

alexhunt7 and others added 3 commits June 12, 2026 19:19
MTP (Multi-Token Prediction) speculative decoding in tensor-parallel mode
was passing raw CUDA tensors directly into the multiprocessing queue,
triggering a cudaMallocAsync IPC failure. The non-TP path now correctly
branches to use the TP dispatcher when TP mode is active.

Problem
-------
Running Qwen3.5 MTP models with speculative decoding in TP mode crashed
with two distinct errors:

1. TP path — cudaMallocAsync IPC failure:

   RuntimeError: cudaMallocAsync does not yet support shareIpcHandle
       File "exllamav3/generator/generator.py", line 530, in iterate_draftmodel_mtp_gen
           batch_logits = self.model.tp_dispatch_lm_head_logits(...)
       File "exllamav3/model/model_tp.py", line 375, in tp_dispatch_lm_head_logits
           self.tp_worker_dispatch(device, fn, args)
       File "exllamav3/model/model_tp.py", line 186, in tp_worker_dispatch
           conn.send((fn, args))
       File "torch/multiprocessing/reductions.py", line ...
           storage._share_cuda_()

   Root cause: params["target_hidden"] was a raw CUDA tensor passed
   directly into the args tuple sent to worker processes. Python's
   multiprocessing pickler invoked PyTorch's reduce_tensor -> _share_cuda_()
   which attempts cudaMallocAsync's shareIpcHandle — an unsupported operation.

2. Non-TP path — missing TP branch:

   The original code had no conditional for TP vs non-TP in
   iterate_draftmodel_mtp_gen. It always called the logits module directly
   via self.model.modules[self.model.logit_layer_idx].forward(...), which
   fails in TP mode because the lm_head shards live in worker processes,
   not on the main process.

Changes
-------
generator.py (iterate_draftmodel_mtp_gen):
  - Added use_tp = self.model.loaded_tp guard before the sampling loop
  - TP branch: construct a minimal tp_params dict containing only
    target_hidden (sent through tp_producer.send()) and attn_mode.
    This routes the tensor through CPU shared memory, avoiding the
    cudaMallocAsync IPC path entirely.
  - Non-TP branch: preserved existing direct-module-forward path unchanged.

model_tp.py (Model_TPMixin):
  - Added tp_dispatch_lm_head_logits() method — mirrors the existing
    tp_dispatch_lm_head_argmax() but returns full logits instead of
    just argmax indices. Handles single-device fast-path and multi-device
    scatter/gather via the bulk gather buffer.

model_tp_fn.py:
  - Added mp_model_forward_lm_head_logits() worker function — receives
    tensors through consumer.recv(), forwards through the sharded
    lm_head module, and optionally gathers partial logits from multiple
    devices into a complete (batch, seq, vocab) tensor.

Why tp_params is minimal
------------------------
Only target_hidden and attn_mode are passed in tp_params because the
MTP logits module (Qwen3_5MtpDecoderLayer) only reads those keys. The
full params dict also contains block_table, cache, and cache_seqlens
which are irrelevant to the lm_head computation. Excluding them reduces
serialization overhead and avoids accidentally sending unpicklable objects.

Verification
------------
- Both modified files pass Python AST parsing (no syntax errors)
- tp_dispatch_lm_head_argmax confirmed unaffected — it is called with
  empty {} params from dflash.py and does not carry CUDA tensors
- target_hidden tensor (~8KB for hidden_size=4096) fits comfortably
  within the 64MB SMProducer buffer, no fallback needed
- Non-TP execution path is structurally unchanged

Testing required
----------------
Multi-GPU TP setup with a Qwen3.5 MTP checkpoint to exercise both the
TP and non-TP branches end-to-end.
@randoentity

Copy link
Copy Markdown
Author

All fixed and working at least from 783bfa7.

@randoentity
randoentity deleted the fix-mtp-tp branch July 16, 2026 09:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants