forked from vllm-project/vllm
-
Notifications
You must be signed in to change notification settings - Fork 30
perf(dspark): add load-aware compact verification capacity #107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
voipmonitor
wants to merge
29
commits into
dev/fathomless-firmament
from
codex/ff-dspark-load-aware-capacity-stack-20260717
Closed
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
0071867
[fix] fix step0 dsd support for dspkv4-dspark
EanWang211123 4b48898
[Spec Decode] Harden DSpark metadata and TP sampling state
voipmonitor 357fa29
[Bugfix][Spec Decode] Mask cache-restored tokens out of DFlash draft …
giorgiopiatti-caffeinated ed5f7c8
Prefer FlashAttn over FlashInfer for SM100f non-causal attention
mgoin 4b248b0
[Spec Decode] Never full-graph-capture non-causal FlashInfer draft at…
mgoin 1d13bf6
fix(spec decode): preserve explicit zero adaptive depth
voipmonitor 7916aaf
spec_decode: retain DFlash CUDA graph backbone outputs
voipmonitor 7a8830e
test(spec decode): use current dynamic-depth predicate
voipmonitor 3571604
fix(dflash): serialize overlapping block-table shift loads
voipmonitor 5d06d3d
style: normalize DSpark correctness tests
voipmonitor 7da3d41
[Spec Decode] DSpark capacity reallocation with varlen full-CG verifi…
LucasWilkinson 1e11f2d
tests: adapt DSpark capacity fixtures to fork InputBatch
voipmonitor 77f3a30
spec_decode: report DSpark capacity verification mode
voipmonitor f0e7c35
spec_decode: pass draft cache state through capacity warmup
voipmonitor eedbe48
spec_decode: keep varlen DSpark verification on full graphs
voipmonitor c12769f
spec_decode: add load-aware DSpark physical depth control
voipmonitor 2f28fde
fix(dspark): harden capacity verification edge paths
voipmonitor b4f6e92
spec_decode: gate DSpark capacity below load knee
voipmonitor 313a77a
fix: canonicalize DSpark capacity across TP ranks
voipmonitor fed18d4
Merge DSpark correctness prerequisite for capacity validation
voipmonitor 43a0a20
fix(dspark): preserve default draft generation width
voipmonitor caa795e
fix(dflash): fail closed on partial restored KV blocks
voipmonitor 7c0d639
Merge branch 'codex/ff-dspark-core-canonical-20260717' into codex/ff-…
voipmonitor edc4976
fix(dspark): harden capacity opt-in and calibration
voipmonitor 21c08ff
fix(indexer): remove duplicate RoPE quant helper
voipmonitor 4cd7055
Merge remote-tracking branch 'origin/codex/ff-sparse-indexer-dedup-20…
voipmonitor c8bff14
style: format touched DSpark helpers
voipmonitor 8fbc196
test(indexer): align B12X fixtures with FF contracts
voipmonitor 8c55386
Merge remote-tracking branch 'lil/codex/ff-sparse-indexer-dedup-20260…
voipmonitor File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # SPDX-FileCopyrightText: Copyright contributors to the vLLM project | ||
| """Profile the engine step-rate curve for the DSpark prefix scheduler. | ||
|
|
||
| Times the captured FULL cudagraph replays of the target verification step at | ||
| every captured batch token count and emits ``dspark_sps_curve`` breakpoints, | ||
| one per capture size. The scheduler linearly interpolates between | ||
| breakpoints, which amortizes cudagraph padding smoothly instead of | ||
| concentrating it into thresholds at capture-size boundaries. | ||
|
|
||
| Example: | ||
| python benchmarks/profile_dspark_sps_curve.py <target-model> \\ | ||
| --speculative-config '{"method": "dspark", "model": "...", ...}' \\ | ||
| --engine-args '{"tensor_parallel_size": 4, "max_num_seqs": 32}' | ||
|
|
||
| Paste the printed ``dspark_sps_curve`` entry into --speculative-config. | ||
|
|
||
| Caveats: replays run on whatever (dummy) buffer state capture left behind, so | ||
| data-dependent kernels (e.g. MoE routing) may be timed on unrepresentative | ||
| inputs, and per-step CPU/draft overhead is modeled only through the constant | ||
| ``--overhead-ms``. Only the curve's shape matters to the scheduler. | ||
| """ | ||
|
|
||
| import argparse | ||
| import json | ||
|
|
||
|
|
||
| def _time_fullgraph_replays(worker, iters: int, warmup: int) -> dict[int, float]: | ||
| """Worker-side: time FULL graph replay per batch token count (ms/step). | ||
|
|
||
| Runs on every TP rank via collective_rpc so the collectives captured in | ||
| the graphs stay matched; every rank replays the same descs in the same | ||
| sorted order. Before timing each descriptor the input buffers are | ||
| refreshed into the same coherent dummy state capture used, so replays | ||
| never read stale metadata. | ||
| """ | ||
| import torch | ||
|
|
||
| from vllm.v1.worker.gpu.cudagraph_utils import prepare_inputs_to_capture | ||
|
|
||
| runner = worker.model_runner | ||
| mgr = runner.cudagraph_manager | ||
| assert mgr is not None and mgr.graphs, ( | ||
| "No FULL cudagraphs captured; run with a cudagraph_mode that captures " | ||
| "FULL decode graphs." | ||
| ) | ||
| # Prefer varlen spec-decode descs; fall back to all captured graphs. | ||
| descs = [d for d in mgr.graphs if d.max_req_tokens is not None] | ||
| if not descs: | ||
| descs = list(mgr.graphs.keys()) | ||
| # One desc per token count: the largest request count is the most | ||
| # representative shape under load. | ||
| by_tokens: dict[int, object] = {} | ||
| for d in descs: | ||
| cur = by_tokens.get(d.num_tokens) | ||
| if cur is None or (d.num_reqs or 0) > (cur.num_reqs or 0): | ||
| by_tokens[d.num_tokens] = d | ||
|
|
||
| results: dict[int, float] = {} | ||
| for num_tokens in sorted(by_tokens): | ||
| desc = by_tokens[num_tokens] | ||
| num_reqs = desc.num_reqs or min(num_tokens, mgr.max_num_reqs) | ||
| prepare_inputs_to_capture( | ||
| num_reqs, | ||
| num_tokens, | ||
| runner.model_state, | ||
| runner.input_buffers, | ||
| runner.block_tables, | ||
| runner.attn_groups, | ||
| runner.kv_cache_config, | ||
| max_req_tokens=desc.max_req_tokens, | ||
| ) | ||
| graph = mgr.graphs[desc] | ||
| for _ in range(warmup): | ||
| graph.replay() | ||
| torch.accelerator.synchronize() | ||
| start = torch.Event(enable_timing=True) | ||
| end = torch.Event(enable_timing=True) | ||
| start.record() | ||
| for _ in range(iters): | ||
| graph.replay() | ||
| end.record() | ||
| torch.accelerator.synchronize() | ||
| results[num_tokens] = start.elapsed_time(end) / iters | ||
| return results | ||
|
|
||
|
|
||
| def curve_breakpoints( | ||
| ms_per_step: dict[int, float], overhead_ms: float | ||
| ) -> list[list[float]]: | ||
| """Convert per-capture-size step times into ``dspark_sps_curve`` | ||
| breakpoints, one per capture size. The scheduler's table linearly | ||
| interpolates between them (and clamps at the ends).""" | ||
| return [ | ||
| [size, round(1000.0 / (ms_per_step[size] + overhead_ms), 3)] | ||
| for size in sorted(ms_per_step) | ||
| ] | ||
|
|
||
|
|
||
| def main() -> None: | ||
| parser = argparse.ArgumentParser(description=__doc__) | ||
| parser.add_argument("model", help="Target model (path or HF id)") | ||
| parser.add_argument( | ||
| "--speculative-config", | ||
| required=True, | ||
| help="JSON speculative config (same value you pass to vllm serve)", | ||
| ) | ||
| parser.add_argument( | ||
| "--engine-args", | ||
| default="{}", | ||
| help="JSON dict of extra vllm.LLM kwargs " | ||
| '(e.g. \'{"tensor_parallel_size": 4, "max_num_seqs": 32}\')', | ||
| ) | ||
| parser.add_argument("--iters", type=int, default=50) | ||
| parser.add_argument("--warmup", type=int, default=5) | ||
| parser.add_argument( | ||
| "--overhead-ms", | ||
| type=float, | ||
| default=0.0, | ||
| help="Constant per-step overhead (draft forward, sampling, CPU gap) " | ||
| "added to every measured step time before converting to a rate.", | ||
| ) | ||
| parser.add_argument("--output", help="Write the curve JSON to this file") | ||
| args = parser.parse_args() | ||
| if args.iters <= 0: | ||
| parser.error("--iters must be greater than zero") | ||
| if args.warmup < 0: | ||
| parser.error("--warmup must be non-negative") | ||
| if args.overhead_ms < 0: | ||
| parser.error("--overhead-ms must be non-negative") | ||
|
|
||
| # The timing callable is shipped to the workers via collective_rpc, which | ||
| # requires the pickle fallback. Local profiling tool, trusted input. | ||
| import os | ||
|
|
||
| os.environ.setdefault("VLLM_ALLOW_INSECURE_SERIALIZATION", "1") | ||
|
|
||
| from vllm import LLM | ||
|
|
||
| llm = LLM( | ||
| model=args.model, | ||
| speculative_config=json.loads(args.speculative_config), | ||
| **json.loads(args.engine_args), | ||
| ) | ||
| per_rank = llm.collective_rpc( | ||
| _time_fullgraph_replays, kwargs={"iters": args.iters, "warmup": args.warmup} | ||
| ) | ||
| ms_per_step = per_rank[0] | ||
|
|
||
| print("\nMeasured FULL-graph step times (rank 0):") | ||
| for size in sorted(ms_per_step): | ||
| print(f" B={size:5d} tokens: {ms_per_step[size]:8.3f} ms/step") | ||
|
|
||
| curve = curve_breakpoints(ms_per_step, args.overhead_ms) | ||
| entry = {"dspark_sps_curve": curve} | ||
| print("\nAdd to --speculative-config:") | ||
| print(json.dumps(entry)) | ||
| if args.output: | ||
| with open(args.output, "w") as f: | ||
| json.dump(entry, f, indent=2) | ||
| print(f"\nWritten to {args.output}") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
tests/evals/gsm8k/configs/DeepSeek-V4-Flash-DSpark-varlen-TP4.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| model_name: "deepseek-ai/DeepSeek-V4-Flash-DSpark" | ||
| accuracy_threshold: 0.92 | ||
| num_questions: 1319 | ||
| num_fewshot: 5 | ||
| startup_max_wait_seconds: 1800 | ||
| server_args: >- | ||
| --tokenizer-mode deepseek_v4 | ||
| --trust-remote-code | ||
| --dtype bfloat16 | ||
| --max-model-len 8192 | ||
| --tensor-parallel-size 4 | ||
| --enable-expert-parallel | ||
| --block-size 256 | ||
| --gpu-memory-utilization 0.5 | ||
| --kv-cache-dtype fp8 | ||
| --max-num-batched-tokens 16384 | ||
| --max-num-seqs 32 | ||
| --speculative-config '{"method":"dspark", | ||
| "model":"deepseek-ai/DeepSeek-V4-Flash-DSpark", | ||
| "attention_backend":"FLASH_ATTN","num_speculative_tokens":7, | ||
| "draft_sample_method":"probabilistic","dspark_confidence_threshold":0.0, | ||
| "dspark_budget_frac":0.5,"dspark_capacity_verification_mode":"varlen"}' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.