Skip to content

Commit 903b453

Browse files
[TRTLLM-12669][perf] bypass rejection sampling when batch is all-greedy
For all-greedy batches, rejection sampling acceptance is equivalent to argmax. Skip it (and the associated draft-prob computation) to save the rejection kernel and a softmax pass. Affects: - _can_use_rejection_sampling in interface.py and eagle3_dynamic_tree.py - _compute_and_store_draft_probs call in eagle3.py Also revert the use_rejection_sampling exception in the populate fast-path since the rejection path is no longer reachable when all-greedy. Signed-off-by: ZhaoyangWang <zhaoyangw@nvidia.com>
1 parent 5a9f690 commit 903b453

3 files changed

Lines changed: 19 additions & 6 deletions

File tree

tensorrt_llm/_torch/speculative/eagle3.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -758,7 +758,10 @@ def _forward_linear_draft_loop(self, inputs, attn_metadata, spec_metadata,
758758
gen_draft_tokens)
759759
next_draft_tokens[num_contexts:] = gen_draft_tokens
760760

761-
if spec_metadata.use_rejection_sampling and draft_logits_list:
761+
# Skip when the whole batch is greedy: _can_use_rejection_sampling will
762+
# bypass the rejection path anyway, so computing draft probs is wasted.
763+
if (spec_metadata.use_rejection_sampling and draft_logits_list
764+
and not spec_metadata.is_all_greedy_sample):
762765
d2t_param = getattr(draft_model.model, "d2t", None)
763766
spec_metadata.d2t = d2t_param.data if d2t_param is not None else None
764767
self._compute_and_store_draft_probs(draft_logits_list,

tensorrt_llm/_torch/speculative/eagle3_dynamic_tree.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,13 @@ def _can_use_rejection_sampling(self, spec_metadata) -> bool:
950950
Returns:
951951
True if rejection sampling is enabled and the draft logit buffer is allocated
952952
"""
953-
return spec_metadata.use_rejection_sampling and self._draft_depth_logits_cat is not None
953+
# Skip rejection sampling when the whole batch is greedy: argmax is
954+
# equivalent and avoids the rejection kernel cost.
955+
return (
956+
spec_metadata.use_rejection_sampling
957+
and self._draft_depth_logits_cat is not None
958+
and not spec_metadata.is_all_greedy_sample
959+
)
954960

955961
def _finalize_dynamic_tree_verify_outputs(
956962
self,

tensorrt_llm/_torch/speculative/interface.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -653,9 +653,10 @@ def _normalize_request_sampling_params(
653653
dtype=torch.float32,
654654
device='cuda')
655655

656-
# All-greedy: argmax branch won't read these buffers, skip H->D copies.
657-
# Rejection sampling always reads them, so it cannot use this fast-path.
658-
if self.is_all_greedy_sample and not self.use_rejection_sampling:
656+
# All-greedy: sampler takes the argmax branch (and rejection sampling
657+
# is also bypassed for all-greedy), so the buffers are never read.
658+
# Skip the H->D copies.
659+
if self.is_all_greedy_sample:
659660
return
660661

661662
# Phase 2: build per-token / per-request lists and copy to GPU.
@@ -1030,8 +1031,11 @@ def _accept_draft_tokens(self, logits, draft_tokens, num_contexts,
10301031

10311032
def _can_use_rejection_sampling(self, spec_metadata: SpecMetadata,
10321033
num_contexts: int) -> bool:
1034+
# Skip rejection sampling when the whole batch is greedy: the
1035+
# accepted result is identical to argmax and the base path is cheaper.
10331036
return (spec_metadata.use_rejection_sampling
1034-
and spec_metadata.draft_probs_valid and num_contexts == 0)
1037+
and spec_metadata.draft_probs_valid and num_contexts == 0
1038+
and not spec_metadata.is_all_greedy_sample)
10351039

10361040
def _sample_and_accept_draft_tokens_rejection(
10371041
self,

0 commit comments

Comments
 (0)