From 5339450ed46a323712409c5878fc03b3aa8e6846 Mon Sep 17 00:00:00 2001 From: saatwiknagpal Date: Thu, 30 Jul 2026 14:10:06 -0700 Subject: [PATCH] Support SGLANG_SIMULATE_ACC_LEN for DFLASH (#32595) --- python/sglang/srt/speculative/dflash_utils.py | 38 +++++++++++++++++++ .../srt/speculative/dflash_worker_v2.py | 35 +++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/python/sglang/srt/speculative/dflash_utils.py b/python/sglang/srt/speculative/dflash_utils.py index 7df8edbd0..30b113167 100644 --- a/python/sglang/srt/speculative/dflash_utils.py +++ b/python/sglang/srt/speculative/dflash_utils.py @@ -12,6 +12,7 @@ import torch.nn.functional as F from sglang.srt.layers.quantization.unquant import UnquantizedLinearMethod from sglang.srt.layers.sampler import apply_custom_logit_processor from sglang.srt.managers.schedule_batch import Req +from sglang.srt.speculative.spec_utils import _sample_simulated_acc_len from sglang.srt.utils import is_cuda, is_musa DEFAULT_DFLASH_MASK_TOKEN = "<|MASK|>" @@ -585,6 +586,43 @@ def compute_dflash_correct_drafts_and_bonus( return correct_len, bonus.to(torch.int64) +def apply_dflash_simulated_acceptance( + *, + candidates: torch.Tensor, + target_predict: Optional[torch.Tensor], + accept_len: torch.Tensor, + commit_lens: torch.Tensor, + bonus: torch.Tensor, + out_tokens: torch.Tensor, + simulate_acc_len: float, + simulate_acc_method: str, + simulate_acc_token_mode: str, + fixed_token_id: int = 100, +) -> None: + """Forces the DFlash acceptance length (SGLANG_SIMULATE_ACC_LEN benchmark knob).""" + block_size = candidates.shape[1] + + # _sample_simulated_acc_len clamps to [1, block_size]. + forced_commit_len = _sample_simulated_acc_len( + simulate_acc_len, simulate_acc_method, block_size + ) + forced_accept_len = forced_commit_len - 1 + + accept_len.fill_(forced_accept_len) + commit_lens.fill_(forced_commit_len) + + if simulate_acc_token_mode != "real-draft-token": + bonus.fill_(fixed_token_id) + out_tokens.fill_(fixed_token_id) + return + + out_tokens.zero_() + if forced_accept_len > 0: + out_tokens[:, :forced_accept_len].copy_(candidates[:, 1:forced_commit_len]) + bonus.copy_(target_predict[:, forced_accept_len].to(dtype=bonus.dtype)) + out_tokens[:, forced_accept_len].copy_(bonus.to(dtype=out_tokens.dtype)) + + def compute_dflash_sampling_correct_drafts_and_bonus( *, candidates: torch.Tensor, diff --git a/python/sglang/srt/speculative/dflash_worker_v2.py b/python/sglang/srt/speculative/dflash_worker_v2.py index 0bfd3b249..fc4b47115 100644 --- a/python/sglang/srt/speculative/dflash_worker_v2.py +++ b/python/sglang/srt/speculative/dflash_worker_v2.py @@ -32,6 +32,7 @@ from sglang.srt.speculative.base_spec_worker import BaseSpecWorker from sglang.srt.speculative.dflash_info import DFlashVerifyInput from sglang.srt.speculative.dflash_info_v2 import DFlashDraftInputV2 from sglang.srt.speculative.dflash_utils import ( + apply_dflash_simulated_acceptance, apply_dflash_verify_logits_adjustments, can_dflash_use_fused_qkv_proj, compute_dflash_correct_drafts_and_bonus, @@ -48,6 +49,9 @@ from sglang.srt.speculative.draft_worker_common import ( ) from sglang.srt.speculative.spec_info import SpeculativeAlgorithm from sglang.srt.speculative.spec_utils import ( + SIMULATE_ACC_LEN, + SIMULATE_ACC_METHOD, + SIMULATE_ACC_TOKEN_MODE, GrammarTree, assign_req_to_token_pool_func, build_grammar_vocab_mask, @@ -1728,6 +1732,9 @@ class DFlashWorkerV2(BaseSpecWorker): candidates = draft_tokens new_seq_lens = None + # Only the greedy branch sets target_predict; the simulated-acceptance + # override below checks for it. + target_predict = None if ( sampling_info is not None and not sampling_info.is_all_greedy @@ -1811,6 +1818,34 @@ class DFlashWorkerV2(BaseSpecWorker): 1, accept_len.to(torch.int64)[:, None], bonus[:, None] ) + if SIMULATE_ACC_LEN > 0: + if SIMULATE_ACC_TOKEN_MODE not in ("fixed", "real-draft-token"): + raise ValueError( + "Invalid SGLANG_SIMULATE_ACC_TOKEN_MODE " + f"{SIMULATE_ACC_TOKEN_MODE!r}; expected 'fixed' or " + "'real-draft-token'." + ) + + if SIMULATE_ACC_TOKEN_MODE == "real-draft-token" and target_predict is None: + # The sampling-verify branch does not materialize the target argmax. + target_predict = torch.argmax( + logits_output.next_token_logits, dim=-1 + ).view(bs, int(self.block_size)) + apply_dflash_simulated_acceptance( + candidates=candidates, + target_predict=target_predict, + accept_len=accept_len, + commit_lens=commit_lens, + bonus=bonus, + out_tokens=out_tokens, + simulate_acc_len=SIMULATE_ACC_LEN, + simulate_acc_method=SIMULATE_ACC_METHOD, + simulate_acc_token_mode=SIMULATE_ACC_TOKEN_MODE, + ) + # The Triton path may have written new_seq_lens from the real + # accept_len; recompute it from the forced commit_lens. + new_seq_lens = None + if self._need_mamba_verify_commit: assert seq_lens_pre_verify is not None self._update_target_mamba_state_after_verify(