Repository navigation
Conversation
|
/bot run |
|
PR_Github #75635 [ run ] triggered by Bot. Commit: |
|
/bot run |
2897ca6 to
4780f93
Compare
|
/bot run |
|
PR_Github #75643 [ run ] triggered by Bot. Commit: |
|
PR_Github #75644 [ run ] triggered by Bot. Commit: |
|
PR_Github #75643 [ run ] completed with state |
|
PR_Github #75635 [ run ] completed with state |
|
PR_Github #75644 [ run ] completed with state
|
…on; "0" = original path) Numerics-preserving trims of the one-model MTP-Eagle draft loop and its step boundary. The gate is read once and cached, so the choice is fixed before CUDA-graph capture; with it off the draft loop is launch-for-launch the original path. - Draft argmax (plain TP, vocab-sharded draft head): one Triton launch computes each rank's (global argmax index, max) pair and writes it straight into the exchange row, replacing torch.max (a single-CTA reduce over the vocab shard) + int32 offset + two float casts + stack. Same tie-break (lowest index) and NaN rule (NaN wins, lowest NaN index) as at::native GreaterOrNan; bf16 is widened bit-exactly in-kernel, so the packed pairs are bit-identical. - The pairs are exchanged through a one-shot MNNVL SUM allreduce over a DEDICATED Lamport workspace (each rank fills its own slot of a zero row, so the fp32 sum is exact) instead of the NCCL allgather. The workspace is private because the one-shot kernel clears the previous call's dirty Lamport buffer with the current launch's threads: on the model's shared workspace a one-row exchange would spend its few threads clearing the preceding model allreduce's payload. The mailbox packs into a persistent staging buffer and is built with the model workspace's own helpers, factored out of distributed/ops.py as pure extractions (_build_mnnvl_workspace: communicator split, multicast buffer, converged construction, protocol init; _launch_mnnvl_allreduce: the kernel launch MNNVLAllReduce.forward also calls); a failed build hands its communicator back like the model workspace does. Falls back to the NCCL allgather off MNNVL, under non-pure-TP worlds (PP/CP/attention DP), above the one-shot payload, and when first reached under CUDA-graph capture; the construction result is converged across the group so every rank takes the same path. - Draft steps >= 1: gather_ids is batch_indices_cuda[:batch] = arange, so one row selection per step turns the hidden-state / position-id row gathers into prefix slices; step 0 selects the hidden-state rows once for both the LM head and the next step; the last step skips the selections nothing consumes. - DSA: update_for_spec_dec before an index-shared draft step (the indexer takes the reuse-TopK path) and after the last draft step skips the DeepGEMM MQA-logits schedule rebuild (schedule launches, 2D/expanded KV-length copies, GVR row order); slot mappings / indptrs are still refreshed. The metadata marks the schedule stale and the indexer raises if a TopK pass ever reads it. The draft loop reaches this through a small runtime-checkable Protocol (MTPIndexerScheduleTrimMetadata) so it stays decoupled from the DSA backend. - _preprocess_inputs: with overlap + spec decode the kv_lens correction calls on_update_kv_lens() again right after the unconditional first call. Metadata whose hook is a pure function of the current seq/kv lengths opts in through kv_lens_hook_idempotent (True on the audited DSA metadata, False on the base class and the DeepSeek-V4 override); for those the first call is fully overwritten by the second and skipped. Tests: test_mtp_tail_trim.py (pack kernel vs torch bitwise incl. ties / -inf / NaN / strided rows / CUDA graph; mocked draft loop trim on vs off bitwise; 2-GPU mailbox vs NCCL allgather through the real draft-token consumer, eager, right after a model-sized allreduce on the shared workspace, and under CUDA-graph replay), test_dsa_indexer mtp_topk_reuse with a stale + poisoned schedule. Signed-off-by: Iman Tabrizian <10105175+tabrizian@users.noreply.github.com>
|
/bot run |
4780f93 to
325ec90
Compare
|
/bot run |
|
PR_Github #75674 [ run ] triggered by Bot. Commit: |
|
PR_Github #75675 [ run ] triggered by Bot. Commit: |
|
PR_Github #75674 [ run ] completed with state |
|
/bot run |
|
PR_Github #75684 [ run ] triggered by Bot. Commit: |
|
PR_Github #75675 [ run ] completed with state |
|
PR_Github #75684 [ run ] completed with state
|
|
/bot run |
|
PR_Github #75711 [ run ] triggered by Bot. Commit: |
|
PR_Github #75711 [ run ] completed with state
|
|
Superseded: split into three self-contained PRs for review, each based independently on main:
Closing this one. |
[None][perf] MTP draft-step glue trim (TRTLLM_MTP_TAIL_TRIM, default on; "0" = original path)
Description
In the one-model MTP-Eagle draft loop a sizeable share of the per-step draft tail is host/launch glue rather than model compute: a single-CTA
torch.maxreduce over each rank's vocab shard followed by cast/offset/stack kernels, an NCCL allgather of the per-rank (index, value) pairs, index-gather and int32->int64 copy kernels for hidden states / position ids that from draft step 1 on selectarange(batch), DeepGEMM MQA-logits schedule rebuilds (schedule launches + KV-length copies + GVR row order) before draft passes that never read them, and a duplicatedon_update_kv_lens()at the step boundary under the overlap scheduler. This PR removes that glue without changing any numeric result.What changed:
tensorrt_llm/_torch/speculative/mtp_tail_trim.py, new): one Triton launch computes each rank's (global argmax index, max value) pair and writes it straight into the exchange row, replacingtorch.max(dim=-1)+ int32 offset + two float casts +stack. Same tie-break (lowest index) and NaN rule (NaN wins, lowest NaN index) asat::nativeGreaterOrNan; bf16 is widened bit-exactly in-kernel and a NaN max is rounded back to the same 0x7FFF bf16 payload torch produces.distributed/ops.pyas pure extractions (_build_mnnvl_workspace: communicator split, multicast buffer, converged construction, protocol init;_launch_mnnvl_allreduce: the kernel launchMNNVLAllReduce.forwardnow also calls), so it works under both the MPI and the Ray (process-group) orchestrators and a failed build hands its communicator back the same way the model workspace does. NCCL allgather remains the path off an MNNVL fabric, under non-pure-TP worlds (PP/CP/attention-DP), above the one-shot payload, and when the exchange is first reached under CUDA-graph capture; the construction result is converged across the group so every rank takes the same path.eagle3.py): from draft step 1 ongather_idsisbatch_indices_cuda[:batch_size] == arange(batch_size), so onerow_selper step turns the hidden-state and position-id row gathers into prefix slices; step 0 selects the hidden-state rows once for both the LM head input and the next step; the last step skips the selections nothing consumes. With the gate off every site keeps its ownhidden_states[gather_ids]gather, launch-for-launch as before.dsa/metadata.py,dsa/indexer.py,sparse/params.py,eagle3.py):update_for_spec_decbefore an index-shared draft step (the indexer takes the reuse-TopK path) and after the last draft step no longer rebuilds the DeepGEMM MQA-logits schedule, the 2D/expanded indexer KV lengths or the GVR row order; slot mappings / indptrs are still refreshed. The metadata marks the schedule stale and the indexer raises if a TopK pass ever reads a stale schedule. The draft loop reaches this through a small runtime-checkable Protocol (MTPIndexerScheduleTrimMetadata:set_skip_indexer_sched_metadata,mtp_next_step_reuses_topk), so it stays decoupled from the DSA backend; whether the following steps reuse TopK is computed once after draft step 0.model_engine.py,attention/backends/interface.py): with overlap scheduling + spec decode,_preprocess_inputscallson_update_kv_lens()twice per step and the first call's work is fully overwritten by the second after the kv_lens correction when the hook is a pure function of the current seq/kv lengths. Metadata classes opt in through an explicitkv_lens_hook_idempotentclass attribute (Trueon the audited DSA metadata,Falseon the base class and on the DeepSeek-V4 metadata, whose override has not been audited); the first call is skipped for those only.Gate:
TRTLLM_MTP_TAIL_TRIM(unset /1= trimmed path,0= original path, read once and cached so the choice is fixed before CUDA-graph capture). The default is on;TRTLLM_MTP_TAIL_TRIM=0gives a same-image A/B. Existing kernels and the non-MTP Eagle3 path are untouched.Numerics: bit-identical. The pack kernel reproduces
_get_local_max_and_combinedbit for bit (ties -> lowest index, NaN wins with the lowest NaN index, bf16 -inf / NaN payloads preserved); the mailbox's fp32 sum of one non-zero slot per rank is exact, the only difference being that a NaN max returns with the canonical NaN payload, which the consuming argmax treats identically. The slice and schedule-skip trims feed every draft step the same tensors as before.Measured (GB300, NVFP4 MoE model, TP8, bs1, MTP depth 5, low-latency disaggregated decode point, decode step ~9 ms; trim on vs
TRTLLM_MTP_TAIL_TRIM=0, same image):Every percentile moved in favour of the trimmed arm; 0 request errors in each arm; identical draft acceptance between arms.
Measurements were taken on a 1.3.0rc28-based branch; this PR is the change rebuilt on
main's MNNVL workspace helpers with the draft-loop hook expressed against theMTPIndexShareMetadata/MTPIndexerScheduleTrimMetadataprotocols.Follow-ups (not in this PR):
checkpoint_prepare/checkpoint_restoredo not yet cover the mailbox's dedicated workspace (the model workspace registry is keyed by mapping only); the ADP + LM-head-TP draft-argmax branch still takes the original_get_local_max_and_combined+ allgather path.Test Coverage
tests/unittest/_torch/speculative/test_mtp_tail_trim.py(new, GPU):test_local_argmax_pack_matches_torch: pack kernel vs_get_local_max_and_combinedbitwise over bf16 / fp16 / fp32, vocab shards 77 .. 151936, rows 1 .. 9, with all-equal rows, ties, all-inf, NaN and+infcolumns, at two rank offsets, in both the[rows, 2]and the mailbox[rows, 128]slot layouts.test_local_argmax_pack_strided_rows_and_cuda_graph: non-contiguous row stride and CUDA-graph capture + replay on fresh inputs.test_draft_loop_trim_is_bit_identical: a mocked MTP-Eagle draft loop with the trim on vs off for draft lengths 1 / 2 / 5 must produce the same draft tokens, feed every draft step the sameinput_ids/position_ids/hidden_states, and leave the same kv_lens after each step (runs on CPU as well).test_mailbox_matches_nccl_allgather(2 GPUs,mpi_pool_executor): dedicated mailbox vs NCCL allgather bitwise and through the real_get_draft_tokens_from_gathered, across row counts including themax_rows + 1NCCL fallback, once directly after a model-sized MNNVL allreduce on the shared workspace (the dedicated-workspace regression check), and under CUDA-graph replay of model allreduce + pack + mailbox on fresh logits.tests/unittest/_torch/attention/sparse/dsa/test_dsa_indexer.py::test_indexer_decode_mtp_topk_reuse: newstale_schedparameter runs the index-shared reuse steps with the MQA-logits schedule marked stale and poisoned (-1fill), asserts the reuse copies the stash 1:1, and asserts a step that computes TopK on the stale schedule raises.PR Checklist