Skip to content

Commit a813f59

Browse files
author
niushengxiao
committed
fix
1 parent 10e4e4a commit a813f59

1 file changed

Lines changed: 34 additions & 111 deletions

File tree

test/benchmark/static_inference/static_benchmark.py

Lines changed: 34 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,9 @@
3434
from lightllm.models.mistral_mtp.model import MistralMTPModel
3535
from lightllm.models.qwen3_moe_mtp.model import Qwen3MOEMTPModel
3636
from lightllm.server.api_cli import make_argument_parser
37-
from lightllm.server.router.model_infer.mode_backend.mtp_pre_process import (
38-
prepare_mtp_prefill_inputs,
39-
)
4037
from lightllm.utils.config_utils import auto_set_fused_shared_experts, get_dtype, get_vocab_size
4138
from lightllm.utils.dist_utils import init_distributed_env
42-
from lightllm.utils.envs_utils import set_env_start_args
39+
from lightllm.utils.envs_utils import get_env_start_args, set_env_start_args
4340

4441

4542
DEFAULT_BATCH_SIZES = [2, 8, 16, 32, 64, 128]
@@ -130,18 +127,6 @@ def empty_multimodal_params(batch_size: int) -> List[Dict]:
130127
return [{"images": [], "audios": []} for _ in range(batch_size)]
131128

132129

133-
def mtp_prefill_chunk_size(batch_size: int, prompt_len: int, batch_max_tokens: int) -> int:
134-
"""Bound each MTP setup prefill step by the production token budget."""
135-
if batch_size <= 0:
136-
raise ValueError(f"batch_size must be positive, got {batch_size}")
137-
if batch_max_tokens < batch_size:
138-
raise ValueError(
139-
"MTP prefill requires at least one token per request in a step: "
140-
f"batch_size={batch_size}, batch_max_tokens={batch_max_tokens}"
141-
)
142-
return max(1, min(int(prompt_len), int(batch_max_tokens) // int(batch_size)))
143-
144-
145130
class StaticBenchmarkExecutor:
146131
def __init__(
147132
self,
@@ -199,115 +184,53 @@ def _run_prefill_case(self, case: BenchmarkCase, warmup: bool) -> BenchmarkResul
199184

200185
def _run_decode_case(self, case: BenchmarkCase, warmup: bool) -> BenchmarkResult:
201186
mtp_enabled = self._mtp_enabled()
202-
token_rows = self.token_source.batch(case.batch_size, case.context_len) if mtp_enabled else None
203187
measured_tokens = case.batch_size * case.output_len
204188
elapsed = 0.0
205-
ttft_elapsed = 0.0
206-
decode_step_count = 0
207189
iters = self._case_iters(warmup)
208190

209191
for _ in range(iters):
210192
self._reset_model_cache()
193+
candidate_width = self._mtp_step_width() if mtp_enabled else 1
194+
req_idx, seq_len, next_ids = self._materialize_context_for_decode(case, candidate_width)
211195
if mtp_enabled:
212-
torch.cuda.synchronize()
213-
ttft_start = time.perf_counter()
214-
req_idx, seq_len, next_ids = self._prefill_for_decode(case, token_rows, mtp_enabled)
215-
torch.cuda.synchronize()
216-
ttft_elapsed += time.perf_counter() - ttft_start
217-
step_elapsed, step_count = self._run_mtp_decode_steps(
196+
elapsed += self._run_mtp_decode_steps(
218197
case=case,
219198
req_idx=req_idx,
220199
seq_len=seq_len,
221200
next_ids=next_ids,
222201
)
223-
elapsed += step_elapsed
224-
decode_step_count += step_count
225202
else:
226-
req_idx, seq_len, next_ids = self._materialize_context_for_decode(case)
227203
elapsed += self._run_plain_decode_steps(
228204
case=case,
229205
req_idx=req_idx,
230206
seq_len=seq_len,
231207
next_ids=next_ids,
232208
)
233-
decode_step_count += case.output_len
234209

235210
self._reset_model_cache()
236-
inter_token_latency_ms = elapsed * 1000.0 / max(1, decode_step_count) if iters > 0 else None
211+
# An MTP verification round may commit multiple tokens; ITL is per
212+
# logical output token so it remains comparable with plain decode.
213+
logical_output_tokens = case.output_len * iters
214+
inter_token_latency_ms = elapsed * 1000.0 / max(1, logical_output_tokens) if iters > 0 else None
237215
return self._make_result(
238216
case,
239217
elapsed,
240218
measured_tokens,
241219
warmup,
242-
ttft_elapsed_s=ttft_elapsed if mtp_enabled else None,
243220
inter_token_latency_ms=inter_token_latency_ms,
244221
)
245222

246-
def _materialize_context_for_decode(self, case: BenchmarkCase):
247-
"""Allocate historical KV slots so decode can be measured without prefill."""
223+
def _materialize_context_for_decode(self, case: BenchmarkCase, candidate_width: int = 1):
224+
"""Allocate synthetic historical KV so decode throughput excludes prefill."""
248225
req_idx = self._alloc_req_indexes(case.batch_size)
249226
self._materialize_cached_prefix(req_idx, case.context_len)
250227
seq_len = cpu_i32_full((case.batch_size,), case.context_len)
251-
next_ids = torch.from_numpy(np.ascontiguousarray(self.token_source.batch(case.batch_size, 1).reshape(-1))).to(
252-
torch.int64
253-
)
254-
return req_idx, seq_len, next_ids
255-
256-
def _prefill_for_decode(self, case: BenchmarkCase, token_rows: np.ndarray, mtp_enabled: bool):
257-
req_idx = self._alloc_req_indexes(case.batch_size)
258-
chunk_size = (
259-
mtp_prefill_chunk_size(case.batch_size, case.context_len, self.args.batch_max_tokens)
260-
if mtp_enabled
261-
else None
262-
)
263-
inputs = self._build_prefill_inputs(
264-
token_rows=token_rows,
265-
req_idx=req_idx,
266-
prompt_len=case.context_len,
267-
chunk_size=chunk_size,
268-
)
269-
output = None
270-
next_ids = None
271-
for model_input in inputs:
272-
output = self._forward_prefill_input(model_input, allow_overlap=not mtp_enabled)
273-
self._touch_output(output)
274-
next_ids = self._argmax_ids(output.logits)
275-
if mtp_enabled:
276-
# Main and draft KV must advance together for every chunk.
277-
next_ids = self._fill_mtp_prefill_kv(case, model_input, output, next_ids)
278-
assert output is not None
279-
assert next_ids is not None
280-
281-
seq_len = cpu_i32_full((case.batch_size,), case.context_len)
228+
tokens = np.ascontiguousarray(self.token_source.batch(case.batch_size, candidate_width))
229+
next_ids = torch.from_numpy(tokens).to(torch.int64)
230+
if candidate_width == 1:
231+
next_ids = next_ids.reshape(-1)
282232
return req_idx, seq_len, next_ids
283233

284-
def _fill_mtp_prefill_kv(
285-
self,
286-
case: BenchmarkCase,
287-
main_prefill_input: ModelInput,
288-
main_output: ModelOutput,
289-
first_next_ids: torch.Tensor,
290-
):
291-
draft_input = main_prefill_input
292-
draft_output = main_output
293-
current_next_ids = first_next_ids.cuda(non_blocking=True)
294-
mtp_candidates = [current_next_ids.detach().cpu()]
295-
for draft_index in range(self._num_mtp_modules()):
296-
draft_input = prepare_mtp_prefill_inputs(
297-
model_input=draft_input,
298-
b_next_token_ids=current_next_ids,
299-
mtp_draft_input_hiddens=draft_output.mtp_main_output_hiddens,
300-
)
301-
draft_output = self.draft_models[draft_index].forward(draft_input)
302-
current_next_ids = self._argmax_ids(draft_output.logits).cuda(non_blocking=True)
303-
mtp_candidates.append(current_next_ids.detach().cpu())
304-
305-
step_width = self._mtp_step_width()
306-
while len(mtp_candidates) < step_width:
307-
mtp_candidates.append(mtp_candidates[-1])
308-
next_ids = torch.stack(mtp_candidates[:step_width], dim=1)
309-
return next_ids
310-
311234
def _run_plain_decode_steps(
312235
self,
313236
case: BenchmarkCase,
@@ -343,13 +266,12 @@ def _run_mtp_decode_steps(
343266
req_idx: torch.Tensor,
344267
seq_len: torch.Tensor,
345268
next_ids: torch.Tensor,
346-
) -> tuple:
269+
) -> float:
347270
elapsed = 0.0
348-
step_count = 0
349271
generated_len = 0
350272
step_width = self._mtp_step_width()
351273
base_req_idx, b_mtp_index = self._build_mtp_decode_index_tensors(req_idx, step_width)
352-
current_candidates = next_ids
274+
current_candidates = next_ids.cuda(non_blocking=True)
353275

354276
while generated_len < case.output_len:
355277
accepted_width = self._sample_mtp_accept_width(step_width, case.output_len - generated_len)
@@ -396,13 +318,11 @@ def _run_mtp_decode_steps(
396318
accepted_width=accepted_width,
397319
)
398320
.detach()
399-
.cpu()
400321
)
401322
seq_len += accepted_width
402323
generated_len += accepted_width
403-
step_count += 1
404324

405-
return elapsed, step_count
325+
return elapsed
406326

407327
def _run_mtp_draft_decode(
408328
self,
@@ -413,7 +333,7 @@ def _run_mtp_draft_decode(
413333
):
414334
draft_input = model_input
415335
draft_output = model_output
416-
draft_next_ids = self._argmax_ids(model_output.logits).cuda(non_blocking=True)
336+
draft_next_ids = torch.argmax(model_output.logits, dim=-1).detach().to(torch.int64)
417337
generated = [draft_next_ids.detach()]
418338

419339
temporary_mem = None
@@ -429,7 +349,7 @@ def _run_mtp_draft_decode(
429349
draft_input.mtp_draft_input_hiddens = draft_output.mtp_main_output_hiddens
430350
draft_model = self.draft_models[step % self._num_mtp_modules()]
431351
draft_output = draft_model.forward(draft_input)
432-
draft_next_ids = self._argmax_ids(draft_output.logits).cuda(non_blocking=True)
352+
draft_next_ids = torch.argmax(draft_output.logits, dim=-1).detach().to(torch.int64)
433353
generated.append(draft_next_ids.detach())
434354

435355
if self.args.mtp_mode.startswith("eagle") and step + 1 < self.args.mtp_step:
@@ -567,7 +487,7 @@ def _make_decode_input(
567487
total_token_num=int(seq_len.sum().item()),
568488
max_q_seq_len=1,
569489
max_kv_seq_len=max_kv_seq_len,
570-
input_ids=input_ids.to(torch.int64).cpu(),
490+
input_ids=input_ids.to(torch.int64),
571491
b_req_idx=req_idx,
572492
b_mtp_index=mtp_index,
573493
b_seq_len=seq_len,
@@ -1212,6 +1132,15 @@ def init_deferred_cudagraph(args: SimpleNamespace, cases: Sequence[BenchmarkCase
12121132
model_kvargs["graph_max_batch_size"] = graph_batch_size
12131133
model_kvargs["disable_cudagraph"] = False
12141134

1135+
decode_batch_sizes = {int(case.batch_size) for case in cases if case.stage == "decode"}
1136+
if decode_batch_sizes == {graph_batch_size}:
1137+
# A single profile case only replays its exact MTP-expanded batch. Avoid
1138+
# spending startup time and graph memory on smaller batches it never uses.
1139+
args.graph_split_batch_size = 0
1140+
args.graph_grow_step_size = graph_batch_size
1141+
set_env_start_args(args)
1142+
get_env_start_args.cache_clear()
1143+
12151144
if args.enable_decode_microbatch_overlap:
12161145
graph_batch_size //= 2
12171146
model.graph_max_batch_size = graph_batch_size * (int(args.mtp_step) + 1)
@@ -1369,13 +1298,6 @@ def _is_dp_group_leader(args: SimpleNamespace, rank_id: int) -> bool:
13691298
return rank_id % _dp_world_size(args) == 0
13701299

13711300

1372-
def _raw_decode_step_count(result: Dict) -> int:
1373-
inter_token_latency_ms = result.get("inter_token_latency_ms")
1374-
if inter_token_latency_ms is None or inter_token_latency_ms <= 0:
1375-
return 0
1376-
return max(1, int(round(float(result["elapsed_ms"]) / float(inter_token_latency_ms))))
1377-
1378-
13791301
def aggregate_rank_results(args: SimpleNamespace, messages: Sequence[Dict]) -> List[Dict]:
13801302
"""Aggregate rank-local measurements into one global result per case."""
13811303
by_case: Dict[int, List[Dict]] = {}
@@ -1416,10 +1338,11 @@ def aggregate_rank_results(args: SimpleNamespace, messages: Sequence[Dict]) -> L
14161338
logical_tokens = batch_size * int(first["context_len"]) * iters
14171339
first["logical_tps"] = logical_tokens / elapsed_s if elapsed_s > 0 else 0.0
14181340

1419-
slowest_item = max(rank_items, key=lambda item: float(item["result"]["elapsed_ms"]))
1420-
decode_step_count = _raw_decode_step_count(slowest_item["result"])
1421-
if decode_step_count > 0:
1422-
first["inter_token_latency_ms"] = elapsed_ms / decode_step_count
1341+
if first["stage"] == "decode" and iters > 0:
1342+
# DP latency follows the slowest rank but keeps the per-token
1343+
# denominator; MTP verification rounds are not output tokens.
1344+
logical_output_tokens = int(first["output_len"]) * iters
1345+
first["inter_token_latency_ms"] = elapsed_ms / max(1, logical_output_tokens)
14231346

14241347
ttft_values = [
14251348
float(item["result"]["ttft_ms"]) for item in rank_items if item["result"].get("ttft_ms") is not None

0 commit comments

Comments
 (0)