Text Generation
Transformers
Safetensors
qwen3
feature-extraction
dflash
speculative-decoding
speculative-decoding-draft
block-diffusion
draft-model
diffusion-language-model
efficiency
qwen
qwen3.5
sglang
custom_code
text-generation-inference
Instructions to use z-lab/Qwen3.5-397B-A17B-DFlash with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use z-lab/Qwen3.5-397B-A17B-DFlash with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="z-lab/Qwen3.5-397B-A17B-DFlash", trust_remote_code=True)# Load model directly from transformers import AutoTokenizer, AutoModel tokenizer = AutoTokenizer.from_pretrained("z-lab/Qwen3.5-397B-A17B-DFlash", trust_remote_code=True) model = AutoModel.from_pretrained("z-lab/Qwen3.5-397B-A17B-DFlash", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use z-lab/Qwen3.5-397B-A17B-DFlash with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "z-lab/Qwen3.5-397B-A17B-DFlash" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "z-lab/Qwen3.5-397B-A17B-DFlash", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/z-lab/Qwen3.5-397B-A17B-DFlash
- SGLang
How to use z-lab/Qwen3.5-397B-A17B-DFlash with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "z-lab/Qwen3.5-397B-A17B-DFlash" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "z-lab/Qwen3.5-397B-A17B-DFlash", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "z-lab/Qwen3.5-397B-A17B-DFlash" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "z-lab/Qwen3.5-397B-A17B-DFlash", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use z-lab/Qwen3.5-397B-A17B-DFlash with Docker Model Runner:
docker model run hf.co/z-lab/Qwen3.5-397B-A17B-DFlash
Upload folder using huggingface_hub
Browse files- README.md +129 -0
- benchmark/README.md +34 -0
- benchmark/patches/apply_patches.py +91 -0
- benchmark/patches/flashinfer-pr-3312.patch +94 -0
- benchmark/results/full-sweep-r5.csv +71 -0
- benchmark/results/full-sweep-r5.raw.jsonl +0 -0
- benchmark/run_benchmark.py +2406 -0
- benchmark/run_modal_benchmark.py +321 -0
- config.json +59 -0
- model.safetensors +3 -0
README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
pipeline_tag: text-generation
|
| 3 |
+
library_name: transformers
|
| 4 |
+
base_model:
|
| 5 |
+
- Qwen/Qwen3.5-397B-A17B
|
| 6 |
+
license: apache-2.0
|
| 7 |
+
inference: false
|
| 8 |
+
tags:
|
| 9 |
+
- dflash
|
| 10 |
+
- speculative-decoding
|
| 11 |
+
- speculative-decoding-draft
|
| 12 |
+
- block-diffusion
|
| 13 |
+
- draft-model
|
| 14 |
+
- diffusion-language-model
|
| 15 |
+
- efficiency
|
| 16 |
+
- qwen
|
| 17 |
+
- qwen3
|
| 18 |
+
- qwen3.5
|
| 19 |
+
- sglang
|
| 20 |
+
---
|
| 21 |
+
|
| 22 |
+
# Qwen3.5-397B-A17B-DFlash
|
| 23 |
+
|
| 24 |
+
[Paper](https://arxiv.org/abs/2602.06036) | [Github](https://github.com/z-lab/dflash) | [Blog](https://z-lab.ai/projects/dflash)
|
| 25 |
+
|
| 26 |
+
This is a joint release collaboration between [Z-Lab](https://z-lab.ai), [Modal](https://modal.com), and [SGLang](https://github.com/sgl-project/sglang). The draft model is mirrored across the following Hugging Face repositories:
|
| 27 |
+
|
| 28 |
+
- [`z-lab/Qwen3.5-397B-A17B-DFlash`](https://huggingface.co/z-lab/Qwen3.5-397B-A17B-DFlash)
|
| 29 |
+
- [`modal-labs/Qwen3.5-397B-A17B-DFlash`](https://huggingface.co/modal-labs/Qwen3.5-397B-A17B-DFlash)
|
| 30 |
+
- [`lmsys/Qwen3.5-397B-A17B-DFlash`](https://huggingface.co/lmsys/Qwen3.5-397B-A17B-DFlash)
|
| 31 |
+
|
| 32 |
+
This repository contains a DFlash draft model for `Qwen/Qwen3.5-397B-A17B`. It is not a standalone language model. It is intended to be paired with the target model in a speculative decoding server.
|
| 33 |
+
|
| 34 |
+
DFlash uses a lightweight block diffusion draft model to propose multiple tokens in parallel. The target model verifies those proposals, improving serving throughput while preserving the target model's output distribution.
|
| 35 |
+
|
| 36 |
+
## Quick Start
|
| 37 |
+
|
| 38 |
+
This model should be used with an inference server that supports DFlash speculative decoding. An example SGLang deployment is:
|
| 39 |
+
|
| 40 |
+
```bash
|
| 41 |
+
export SGLANG_ENABLE_OVERLAP_PLAN_STREAM=1
|
| 42 |
+
|
| 43 |
+
python -m sglang.launch_server \
|
| 44 |
+
--model-path Qwen/Qwen3.5-397B-A17B \
|
| 45 |
+
--trust-remote-code \
|
| 46 |
+
--speculative-algorithm DFLASH \
|
| 47 |
+
--speculative-draft-model-path z-lab/Qwen3.5-397B-A17B-DFlash \
|
| 48 |
+
--speculative-dflash-block-size 8 \
|
| 49 |
+
--speculative-draft-attention-backend fa4 \
|
| 50 |
+
--attention-backend trtllm_mha \
|
| 51 |
+
--linear-attn-prefill-backend triton \
|
| 52 |
+
--linear-attn-decode-backend flashinfer \
|
| 53 |
+
--mamba-scheduler-strategy extra_buffer \
|
| 54 |
+
--tp-size 8 \
|
| 55 |
+
--max-running-requests 32 \
|
| 56 |
+
--cuda-graph-max-bs-decode 32 \
|
| 57 |
+
--cuda-graph-backend-prefill tc_piecewise \
|
| 58 |
+
--enable-flashinfer-allreduce-fusion \
|
| 59 |
+
--mem-fraction-static 0.8 \
|
| 60 |
+
--host 0.0.0.0 \
|
| 61 |
+
--port 30000
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
Block size `8` is the recommended default for higher-concurrency serving. Block size `16` gives longer accept lengths and the best concurrency-1 throughput in most workloads.
|
| 65 |
+
|
| 66 |
+
## Benchmark Results
|
| 67 |
+
|
| 68 |
+
We benchmarked DFlash against the autoregressive baseline and Qwen3.5's built-in MTP draft path. DFlash reaches up to `4.31x` speedup at concurrency 1 and `2.77x` at concurrency 32. Across the benchmark suite, DFlash delivers higher throughput than MTP at every tested setting.
|
| 69 |
+
|
| 70 |
+
### Setup
|
| 71 |
+
|
| 72 |
+
- Runtime: SGLang on 8x NVIDIA B200 GPUs, tensor parallel size 8, `bfloat16`
|
| 73 |
+
- Backends: `trtllm_mha` target attention, `fa4` DFlash draft attention, `flashinfer` linear attention
|
| 74 |
+
- Workloads: GSM8K, MATH500, HumanEval, MBPP, and MT-Bench with the Qwen chat template
|
| 75 |
+
- Decoding: greedy, thinking enabled, max output length 4096 tokens
|
| 76 |
+
- Measurement: 5 independent runs per configuration at concurrency 1 and 32 with continuous batching
|
| 77 |
+
- Throughput: generated output tokens / wall-clock benchmark time, including prefill and scheduling
|
| 78 |
+
- Accept length: `completion_tokens / spec_verify_ct` per generation turn, averaged across generation turns
|
| 79 |
+
|
| 80 |
+
For reproduction, the evaluation scripts, Modal runner, runtime patches, and raw benchmark outputs are included under [`benchmark/`](https://huggingface.co/modal-labs/Qwen3.5-397B-A17B-DFlash/tree/main/benchmark).
|
| 81 |
+
|
| 82 |
+
### Throughput and Speedup
|
| 83 |
+
|
| 84 |
+
Each cell is `output tok/s (speedup)`. Bold marks the fastest speculative configuration in each row.
|
| 85 |
+
|
| 86 |
+
#### Concurrency 1
|
| 87 |
+
|
| 88 |
+
| Workload | Baseline | MTP steps=3 | DFlash block=4 | MTP steps=7 | DFlash block=8 | MTP steps=15 | DFlash block=16 |
|
| 89 |
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
| 90 |
+
| gsm8k | 204.6 (1.00x) | 484.5 (2.37x) | 530.0 (2.59x) | 536.7 (2.62x) | 689.0 (3.37x) | 439.2 (2.15x) | **711.3 (3.48x)** |
|
| 91 |
+
| math500 | 204.4 (1.00x) | 505.2 (2.47x) | 551.3 (2.70x) | 589.4 (2.88x) | 762.6 (3.73x) | 499.8 (2.44x) | **831.9 (4.07x)** |
|
| 92 |
+
| humaneval | 202.9 (1.00x) | 483.0 (2.38x) | 543.6 (2.68x) | 557.9 (2.75x) | 752.5 (3.71x) | 480.5 (2.37x) | **874.6 (4.31x)** |
|
| 93 |
+
| mbpp | 204.6 (1.00x) | 487.4 (2.38x) | 550.3 (2.69x) | 543.7 (2.66x) | 751.2 (3.67x) | 445.6 (2.18x) | **807.8 (3.95x)** |
|
| 94 |
+
| mt-bench | 202.6 (1.00x) | 442.9 (2.19x) | 473.6 (2.34x) | 441.5 (2.18x) | **545.9 (2.69x)** | 338.0 (1.67x) | 515.3 (2.54x) |
|
| 95 |
+
|
| 96 |
+
#### Concurrency 32
|
| 97 |
+
|
| 98 |
+
| Workload | Baseline | MTP steps=3 | DFlash block=4 | MTP steps=7 | DFlash block=8 | MTP steps=15 | DFlash block=16 |
|
| 99 |
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
| 100 |
+
| gsm8k | 2597.7 (1.00x) | 4462.3 (1.72x) | 5078.0 (1.95x) | 4902.9 (1.89x) | **6158.9 (2.37x)** | 4079.5 (1.57x) | 5648.8 (2.17x) |
|
| 101 |
+
| math500 | 2615.5 (1.00x) | 4838.6 (1.85x) | 5318.6 (2.03x) | 5601.6 (2.14x) | **6910.5 (2.64x)** | 4855.0 (1.86x) | 6857.8 (2.62x) |
|
| 102 |
+
| humaneval | 2452.7 (1.00x) | 3838.1 (1.56x) | 5081.4 (2.07x) | 4347.4 (1.77x) | 6666.0 (2.72x) | 3917.2 (1.60x) | **6783.7 (2.77x)** |
|
| 103 |
+
| mbpp | 2582.3 (1.00x) | 4129.8 (1.60x) | 5265.7 (2.04x) | 4461.0 (1.73x) | **6681.4 (2.59x)** | 3783.7 (1.47x) | 6344.1 (2.46x) |
|
| 104 |
+
| mt-bench | 2537.3 (1.00x) | 4024.6 (1.59x) | 4425.3 (1.74x) | 3991.7 (1.57x) | **4763.2 (1.88x)** | 3100.3 (1.22x) | 4016.0 (1.58x) |
|
| 105 |
+
|
| 106 |
+
### Accept Length
|
| 107 |
+
|
| 108 |
+
Mean accept length at concurrency 1. Bold marks the higher value in each matched MTP/DFlash pair.
|
| 109 |
+
|
| 110 |
+
| Workload | MTP steps=3 | DFlash block=4 | MTP steps=7 | DFlash block=8 | MTP steps=15 | DFlash block=16 |
|
| 111 |
+
| --- | --- | --- | --- | --- | --- | --- |
|
| 112 |
+
| gsm8k | **3.578** | 3.548 | 5.612 | **5.738** | 7.006 | **7.696** |
|
| 113 |
+
| math500 | **3.636** | 3.622 | 5.810 | **6.003** | 7.294 | **8.212** |
|
| 114 |
+
| humaneval | 3.643 | **3.697** | 5.893 | **6.292** | 7.541 | **9.344** |
|
| 115 |
+
| mbpp | 3.532 | **3.588** | 5.357 | **5.860** | 6.398 | **7.801** |
|
| 116 |
+
| mt-bench | **3.244** | 3.188 | 4.567 | **4.584** | 5.256 | **5.523** |
|
| 117 |
+
|
| 118 |
+
## Citation
|
| 119 |
+
|
| 120 |
+
If you find DFlash useful, please cite the original paper:
|
| 121 |
+
|
| 122 |
+
```bibtex
|
| 123 |
+
@article{chen2026dflash,
|
| 124 |
+
title = {{DFlash: Block Diffusion for Flash Speculative Decoding}},
|
| 125 |
+
author = {Chen, Jian and Liang, Yesheng and Liu, Zhijian},
|
| 126 |
+
journal = {arXiv preprint arXiv:2602.06036},
|
| 127 |
+
year = {2026}
|
| 128 |
+
}
|
| 129 |
+
```
|
benchmark/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Benchmark Reproduction
|
| 2 |
+
|
| 3 |
+
This folder contains the benchmark harness, Modal runner, runtime patch, and raw outputs used for the model card tables.
|
| 4 |
+
|
| 5 |
+
These scripts are intended to reproduce the release benchmark. They are not a general-purpose benchmark framework, and they should not be treated as a reference for Modal application structure or production Modal best practices.
|
| 6 |
+
|
| 7 |
+
## Full Sweep
|
| 8 |
+
|
| 9 |
+
The full sweep runs baseline, MTP, and DFlash across all workloads at concurrency 1 and 32, with 5 independent runs per configuration. It uses up to 32 Modal containers, each with 8 B200 GPUs.
|
| 10 |
+
|
| 11 |
+
This is a large benchmark and can take a long time to complete.
|
| 12 |
+
|
| 13 |
+
```bash
|
| 14 |
+
modal run run_modal_benchmark.py \
|
| 15 |
+
--workloads all \
|
| 16 |
+
--concurrencies 1,32 \
|
| 17 |
+
--spec-modes mtp,dflash \
|
| 18 |
+
--mtp-num-steps 3,7,15 \
|
| 19 |
+
--dflash-draft-model modal-labs/Qwen3.5-397B-A17B-DFlash \
|
| 20 |
+
--dflash-block-sizes 4,8,16 \
|
| 21 |
+
--runs-per-config 5 \
|
| 22 |
+
--raw-output results/full-sweep-r5.raw.jsonl \
|
| 23 |
+
--csv-output results/full-sweep-r5.csv
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
The generated CSV and raw JSONL are written under `results/`. The raw JSONL contains per-run records; the CSV contains aggregated rows used by the README tables.
|
| 27 |
+
|
| 28 |
+
## Files
|
| 29 |
+
|
| 30 |
+
- `run_benchmark.py`: local benchmark harness and result aggregation.
|
| 31 |
+
- `run_modal_benchmark.py`: Modal entrypoint that dispatches one benchmark job per configuration.
|
| 32 |
+
- `patches/`: runtime patch helper and FlashInfer patch used by the reproduced environment.
|
| 33 |
+
- `results/full-sweep-r5.csv`: aggregate benchmark results.
|
| 34 |
+
- `results/full-sweep-r5.raw.jsonl`: raw per-run benchmark records.
|
benchmark/patches/apply_patches.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import importlib.util
|
| 4 |
+
import os
|
| 5 |
+
import subprocess
|
| 6 |
+
import sys
|
| 7 |
+
from dataclasses import dataclass
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
PATCH_DIR = Path(os.environ.get("MODAL_PATCH_DIR", "/root/patches"))
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
@dataclass(frozen=True)
|
| 15 |
+
class PatchSpec:
|
| 16 |
+
name: str
|
| 17 |
+
module: str
|
| 18 |
+
patch_file: str
|
| 19 |
+
strip: int
|
| 20 |
+
includes: tuple[str, ...] = ()
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
PATCHES = (
|
| 24 |
+
PatchSpec(
|
| 25 |
+
name="flashinfer-pr-3312",
|
| 26 |
+
module="flashinfer",
|
| 27 |
+
patch_file="flashinfer-pr-3312.patch",
|
| 28 |
+
strip=1,
|
| 29 |
+
),
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def _package_parent(module_name: str) -> Path:
|
| 34 |
+
spec = importlib.util.find_spec(module_name)
|
| 35 |
+
if spec is None or spec.submodule_search_locations is None:
|
| 36 |
+
raise RuntimeError(f"Could not find installed package {module_name!r}.")
|
| 37 |
+
locations = list(spec.submodule_search_locations)
|
| 38 |
+
if not locations:
|
| 39 |
+
raise RuntimeError(f"Installed package {module_name!r} has no package path.")
|
| 40 |
+
return Path(locations[0]).resolve().parent
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def _git_apply_command(spec: PatchSpec, patch_path: Path) -> list[str]:
|
| 44 |
+
cmd = ["git", "apply", f"-p{spec.strip}"]
|
| 45 |
+
for include in spec.includes:
|
| 46 |
+
cmd.append(f"--include={include}")
|
| 47 |
+
cmd.append(str(patch_path))
|
| 48 |
+
return cmd
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _check(cmd: list[str], *, cwd: Path) -> subprocess.CompletedProcess[str]:
|
| 52 |
+
return subprocess.run(
|
| 53 |
+
cmd,
|
| 54 |
+
cwd=cwd,
|
| 55 |
+
text=True,
|
| 56 |
+
stdout=subprocess.PIPE,
|
| 57 |
+
stderr=subprocess.STDOUT,
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def _apply_patch(spec: PatchSpec) -> None:
|
| 62 |
+
patch_path = PATCH_DIR / spec.patch_file
|
| 63 |
+
if not patch_path.exists():
|
| 64 |
+
raise RuntimeError(f"Missing patch file: {patch_path}")
|
| 65 |
+
|
| 66 |
+
cwd = _package_parent(spec.module)
|
| 67 |
+
base_cmd = _git_apply_command(spec, patch_path)
|
| 68 |
+
reverse_cmd = [*base_cmd[:2], "--reverse", "--check", *base_cmd[2:]]
|
| 69 |
+
check_cmd = [*base_cmd[:2], "--check", *base_cmd[2:]]
|
| 70 |
+
|
| 71 |
+
reverse = _check(reverse_cmd, cwd=cwd)
|
| 72 |
+
if reverse.returncode == 0:
|
| 73 |
+
print(f"[patch] {spec.name} already applied under {cwd}")
|
| 74 |
+
return
|
| 75 |
+
|
| 76 |
+
check = _check(check_cmd, cwd=cwd)
|
| 77 |
+
if check.returncode != 0:
|
| 78 |
+
print(check.stdout, file=sys.stderr)
|
| 79 |
+
raise RuntimeError(f"Patch {spec.name} does not apply under {cwd}.")
|
| 80 |
+
|
| 81 |
+
print(f"[patch] applying {spec.name} under {cwd}")
|
| 82 |
+
subprocess.run(base_cmd, cwd=cwd, check=True)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def main() -> None:
|
| 86 |
+
for patch in PATCHES:
|
| 87 |
+
_apply_patch(patch)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
if __name__ == "__main__":
|
| 91 |
+
main()
|
benchmark/patches/flashinfer-pr-3312.patch
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
From 8c8f152595c11316bc72166929819c56bd3acdb8 Mon Sep 17 00:00:00 2001
|
| 2 |
+
From: William Hu <willhu@modal.com>
|
| 3 |
+
Date: Wed, 13 May 2026 15:19:48 +0000
|
| 4 |
+
Subject: [PATCH] Fix Blackwell GDN chunked kernel compilation
|
| 5 |
+
|
| 6 |
+
---
|
| 7 |
+
.../blackwell/gated_delta_net_chunked.py | 18 +++++++++---------
|
| 8 |
+
1 file changed, 9 insertions(+), 9 deletions(-)
|
| 9 |
+
|
| 10 |
+
diff --git a/flashinfer/gdn_kernels/blackwell/gated_delta_net_chunked.py b/flashinfer/gdn_kernels/blackwell/gated_delta_net_chunked.py
|
| 11 |
+
index 53fe44ce55..2c22c8e120 100644
|
| 12 |
+
--- a/flashinfer/gdn_kernels/blackwell/gated_delta_net_chunked.py
|
| 13 |
+
+++ b/flashinfer/gdn_kernels/blackwell/gated_delta_net_chunked.py
|
| 14 |
+
@@ -2403,7 +2403,7 @@ def compute_group_0(
|
| 15 |
+
|
| 16 |
+
tKKrKK_out = cute.make_rmem_tensor_like(tKKrKK, self.io_dtype)
|
| 17 |
+
tCrAI = tiled_ainv_r2s.retile(tKKrKK_out)
|
| 18 |
+
- for sub in cutlass.range(tKKrKK.shape[2]):
|
| 19 |
+
+ for sub in cutlass.range_constexpr(tKKrKK.shape[2]):
|
| 20 |
+
cute.copy(
|
| 21 |
+
tiled_shared_t2r,
|
| 22 |
+
tTR_tStS[None, 0, sub, kk_handle.index],
|
| 23 |
+
@@ -2443,7 +2443,7 @@ def compute_group_0(
|
| 24 |
+
# Convert fp32 tQKrQK -> fp16 and store to sQk, one subtile at a time.
|
| 25 |
+
tQKrQK_out = cute.make_rmem_tensor_like(tQKrQK, self.io_dtype)
|
| 26 |
+
tCrQK = tiled_qk_r2s.retile(tQKrQK_out)
|
| 27 |
+
- for sub in cutlass.range(tQKrQK.shape[2]):
|
| 28 |
+
+ for sub in cutlass.range_constexpr(tQKrQK.shape[2]):
|
| 29 |
+
cute.copy(
|
| 30 |
+
tiled_shared_t2r,
|
| 31 |
+
tTR_tStS[None, 0, sub, qk_handle.index],
|
| 32 |
+
@@ -2979,7 +2979,7 @@ def _load_initial_state(
|
| 33 |
+
)[None, None, 0, 0]
|
| 34 |
+
tGR_tCgState = thr_state_r2t.partition_S(gS_init)
|
| 35 |
+
kv_acc_handle = kv_acc_producer.acquire_and_advance()
|
| 36 |
+
- for sub in cutlass.range(tRT_tCrState.shape[2]):
|
| 37 |
+
+ for sub in cutlass.range_constexpr(tRT_tCrState.shape[2]):
|
| 38 |
+
# 1. Load S_init fp32 GMEM -> fp32 registers
|
| 39 |
+
cute.autovec_copy(
|
| 40 |
+
tGR_tCgState[None, 0, sub],
|
| 41 |
+
@@ -3063,7 +3063,7 @@ def _store_final_state(
|
| 42 |
+
# Wait for last GEMM-7 to finish
|
| 43 |
+
kv_acc_handle = kv_acc_consumer.wait_and_advance()
|
| 44 |
+
|
| 45 |
+
- for sub in cutlass.range(tTR_rState.shape[2]):
|
| 46 |
+
+ for sub in cutlass.range_constexpr(tTR_rState.shape[2]):
|
| 47 |
+
# Read state TMEM -> fp32 registers
|
| 48 |
+
cute.copy(
|
| 49 |
+
tiled_state_t2r,
|
| 50 |
+
@@ -3344,7 +3344,7 @@ def compute_group_1(
|
| 51 |
+
kv_handle = kv_acc_consumer.wait_and_advance()
|
| 52 |
+
|
| 53 |
+
state_inp_ready_handle = state_inp_ready_producer.acquire_and_advance()
|
| 54 |
+
- for sub in cutlass.range(tRT_rState_inp.shape[2]):
|
| 55 |
+
+ for sub in cutlass.range_constexpr(tRT_rState_inp.shape[2]):
|
| 56 |
+
cute.copy(
|
| 57 |
+
tiled_state_t2r,
|
| 58 |
+
tTR_tCtState[None, 0, sub, kv_handle.index],
|
| 59 |
+
@@ -3386,7 +3386,7 @@ def compute_group_1(
|
| 60 |
+
state_inp_ready_handle.commit()
|
| 61 |
+
|
| 62 |
+
# Load S_prev -> scale by Phi -> write Phi*S_prev back to same TMEM slot.
|
| 63 |
+
- for sub in cutlass.range(tTR_rState.shape[2]):
|
| 64 |
+
+ for sub in cutlass.range_constexpr(tTR_rState.shape[2]):
|
| 65 |
+
for k in cutlass.range(sub_tile_size, vectorize=True):
|
| 66 |
+
tTR_rState[k, 0, sub] = tTR_rState[k, 0, sub] * cumprod_total
|
| 67 |
+
cute.copy(
|
| 68 |
+
@@ -3471,7 +3471,7 @@ def compute_group_1(
|
| 69 |
+
# Write scaled result back to same q_state TMEM slot so GEMM 6 accumulates on top.
|
| 70 |
+
if cutlass.const_expr(valid_state):
|
| 71 |
+
qs_handle = q_state_acc_consumer.wait_and_advance()
|
| 72 |
+
- for sub in cutlass.range(tTR_rQS.shape[1]):
|
| 73 |
+
+ for sub in cutlass.range_constexpr(tTR_rQS.shape[1]):
|
| 74 |
+
cute.copy(
|
| 75 |
+
tiled_qs_t2r,
|
| 76 |
+
tTR_tCtQS[None, sub, 0, qs_handle.index],
|
| 77 |
+
@@ -3499,7 +3499,7 @@ def compute_group_1(
|
| 78 |
+
|
| 79 |
+
tTR_rNv = cute.make_rmem_tensor_like(tTR_tCcShared, self.acc_dtype)
|
| 80 |
+
tTR_rNv_inp = cute.make_rmem_tensor_like(tTR_rNv, self.io_dtype)
|
| 81 |
+
- for sub in cutlass.range(tTR_rNv.shape[1]):
|
| 82 |
+
+ for sub in cutlass.range_constexpr(tTR_rNv.shape[1]):
|
| 83 |
+
cute.copy(
|
| 84 |
+
tiled_shared_t2r,
|
| 85 |
+
tTR_tCtShared[None, sub, 0, nv_handle.index],
|
| 86 |
+
@@ -3523,7 +3523,7 @@ def compute_group_1(
|
| 87 |
+
decay_v_handle = shared_inp_ready_producer.acquire_and_advance()
|
| 88 |
+
tTR_rDv = tTR_rNv
|
| 89 |
+
tRT_rDv_inp = cute.make_rmem_tensor_like(tTR_rDv, self.io_dtype)
|
| 90 |
+
- for sub in cutlass.range(tTR_rDv.shape[1]):
|
| 91 |
+
+ for sub in cutlass.range_constexpr(tTR_rDv.shape[1]):
|
| 92 |
+
for k in cutlass.range(sub_tile_size, vectorize=True):
|
| 93 |
+
tTR_rDv[k, sub, 0] = tTR_rDv[k, sub, 0] * tGrDecayScale[k, sub, 0]
|
| 94 |
+
tRT_rDv_inp[None, sub, 0].store(
|
benchmark/results/full-sweep-r5.csv
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
workload,backend,tp,mode,mtp_num_steps,dflash_block_size,concurrency,source_sample_count,source_generation_turn_count,runs_per_config,successful_runs,failed_runs,status,successful_run_numbers,failed_run_numbers,failure_messages,measured_sample_count,measured_generation_turn_count,output_toks_per_s,output_toks_per_s_std,latency_s,latency_s_std,output_tokens,speedup_vs_baseline,accept_length_mean_from_conc1,accept_length_mean_this_conc,accept_length_mean_this_conc_std,spec_verify_ct_sum
|
| 2 |
+
gsm8k,trtllm_mha,8,baseline,,,1,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,204.555526,2.145455,12635.580474,132.149628,2584451,,,,,0
|
| 3 |
+
gsm8k,trtllm_mha,8,baseline,,,32,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,2597.659928,4.218969,1000.151140,1.480044,2598050,,,,,0
|
| 4 |
+
math500,trtllm_mha,8,baseline,,,1,500,500,5,5,0,ok,"1,2,3,4,5",,,500,500,204.419625,2.159273,8028.813504,85.127007,1641100,,,,,0
|
| 5 |
+
math500,trtllm_mha,8,baseline,,,32,500,500,5,5,0,ok,"1,2,3,4,5",,,1500,1500,2615.492879,4.644475,1877.009292,4.043241,4909295,,,,,0
|
| 6 |
+
humaneval,trtllm_mha,8,baseline,,,1,164,164,5,5,0,ok,"1,2,3,4,5",,,164,164,202.857238,2.560204,708.993392,9.004063,143806,,,,,0
|
| 7 |
+
humaneval,trtllm_mha,8,baseline,,,32,164,164,5,5,0,ok,"1,2,3,4,5",,,1148,1148,2452.728100,6.909290,444.353322,2.704221,1089875,,,,,0
|
| 8 |
+
mbpp,trtllm_mha,8,baseline,,,1,257,257,5,5,0,ok,"1,2,3,4,5",,,257,257,204.627794,2.490696,1431.877214,17.502916,292967,,,,,0
|
| 9 |
+
mbpp,trtllm_mha,8,baseline,,,32,257,257,5,5,0,ok,"1,2,3,4,5",,,1028,1028,2582.331962,16.758001,457.544569,3.271579,1181489,,,,,0
|
| 10 |
+
mt-bench,trtllm_mha,8,baseline,,,1,80,160,5,5,0,ok,"1,2,3,4,5",,,80,160,202.627315,2.064800,1838.911437,18.577677,372583,,,,,0
|
| 11 |
+
mt-bench,trtllm_mha,8,baseline,,,32,80,160,5,5,0,ok,"1,2,3,4,5",,,560,1120,2537.280069,5.056329,1035.679885,6.266022,2627822,,,,,0
|
| 12 |
+
gsm8k,trtllm_mha,8,mtp_s3,3,,1,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,484.515466,1.092627,5333.599683,12.056415,2584201,2.368626,3.578206,3.578206,0.000000,741348
|
| 13 |
+
gsm8k,trtllm_mha,8,mtp_s3,3,,32,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,4462.277886,15.165587,582.052122,4.740478,2597311,1.717807,3.578206,3.575976,0.002168,745479
|
| 14 |
+
math500,trtllm_mha,8,mtp_s3,3,,1,500,500,5,5,0,ok,"1,2,3,4,5",,,500,500,505.220064,0.998658,3243.616450,6.397895,1638735,2.471485,3.636082,3.636082,0.000000,452103
|
| 15 |
+
math500,trtllm_mha,8,mtp_s3,3,,32,500,500,5,5,0,ok,"1,2,3,4,5",,,1500,1500,4838.626174,12.753263,1026.170872,6.286706,4965232,1.849986,3.636082,3.625503,0.002359,1374541
|
| 16 |
+
humaneval,trtllm_mha,8,mtp_s3,3,,1,164,164,5,5,0,ok,"1,2,3,4,5",,,164,164,482.971992,0.848283,291.487600,0.511024,140780,2.380847,3.642722,3.642722,0.000000,39031
|
| 17 |
+
humaneval,trtllm_mha,8,mtp_s3,3,,32,164,164,5,5,0,ok,"1,2,3,4,5",,,1148,1148,3838.116080,27.625345,274.271199,4.884137,1052625,1.564836,3.642722,3.639766,0.004242,291133
|
| 18 |
+
mbpp,trtllm_mha,8,mtp_s3,3,,1,257,257,5,5,0,ok,"1,2,3,4,5",,,257,257,487.387355,1.119903,590.364547,1.356427,287735,2.381824,3.532388,3.532388,0.000000,81640
|
| 19 |
+
mbpp,trtllm_mha,8,mtp_s3,3,,32,257,257,5,5,0,ok,"1,2,3,4,5",,,1028,1028,4129.797287,36.155174,286.132210,3.022273,1181583,1.599251,3.532388,3.529203,0.000768,335234
|
| 20 |
+
mt-bench,trtllm_mha,8,mtp_s3,3,,1,80,160,5,5,0,ok,"1,2,3,4,5",,,80,160,442.878488,0.540885,795.239395,0.971336,352194,2.185680,3.243687,3.243687,0.000000,110843
|
| 21 |
+
mt-bench,trtllm_mha,8,mtp_s3,3,,32,80,160,5,5,0,ok,"1,2,3,4,5",,,560,1120,4024.576377,25.738403,646.195740,3.841449,2600641,1.586177,3.243687,3.246441,0.002884,817461
|
| 22 |
+
gsm8k,trtllm_mha,8,mtp_s7,7,,1,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,536.722583,0.679534,4919.137870,6.225032,2640209,2.623848,5.612091,5.612091,0.000000,503221
|
| 23 |
+
gsm8k,trtllm_mha,8,mtp_s7,7,,32,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,4902.882902,12.827666,538.479681,5.171311,2640066,1.887423,5.612091,5.626037,0.006586,501991
|
| 24 |
+
math500,trtllm_mha,8,mtp_s7,7,,1,500,500,5,5,0,ok,"1,2,3,4,5",,,500,500,589.358698,1.263260,2788.186599,5.961230,1643236,2.883083,5.810442,5.810442,0.000000,285934
|
| 25 |
+
math500,trtllm_mha,8,mtp_s7,7,,32,500,500,5,5,0,ok,"1,2,3,4,5",,,1500,1500,5601.600738,16.440313,889.261794,2.935187,4981255,2.141700,5.810442,5.803398,0.002263,867913
|
| 26 |
+
humaneval,trtllm_mha,8,mtp_s7,7,,1,164,164,5,5,0,ok,"1,2,3,4,5",,,164,164,557.878026,1.239829,255.434983,0.568559,142501,2.750102,5.892962,5.892962,0.000000,24985
|
| 27 |
+
humaneval,trtllm_mha,8,mtp_s7,7,,32,164,164,5,5,0,ok,"1,2,3,4,5",,,1148,1148,4347.409011,39.265600,241.632788,1.833643,1050421,1.772479,5.892962,5.879749,0.015840,181992
|
| 28 |
+
mbpp,trtllm_mha,8,mtp_s7,7,,1,257,257,5,5,0,ok,"1,2,3,4,5",,,257,257,543.663939,1.714676,547.506183,1.726410,297657,2.656843,5.357283,5.357283,0.000000,55728
|
| 29 |
+
mbpp,trtllm_mha,8,mtp_s7,7,,32,257,257,5,5,0,ok,"1,2,3,4,5",,,1028,1028,4461.002797,39.420307,263.953747,2.570069,1177424,1.727509,5.357283,5.345784,0.004161,221558
|
| 30 |
+
mt-bench,trtllm_mha,8,mtp_s7,7,,1,80,160,5,5,0,ok,"1,2,3,4,5",,,80,160,441.534700,1.301296,825.671311,2.434773,364560,2.179048,4.566832,4.566832,0.000000,85056
|
| 31 |
+
mt-bench,trtllm_mha,8,mtp_s7,7,,32,80,160,5,5,0,ok,"1,2,3,4,5",,,560,1120,3991.741300,32.936341,647.364770,5.132053,2584055,1.573236,4.566832,4.541887,0.004682,602291
|
| 32 |
+
gsm8k,trtllm_mha,8,mtp_s15,15,,1,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,439.221305,1.229396,5995.457146,16.788503,2633316,2.147198,7.006249,7.006249,0.000000,420009
|
| 33 |
+
gsm8k,trtllm_mha,8,mtp_s15,15,,32,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,4079.495401,11.679675,633.391910,2.207605,2583916,1.570450,7.006249,7.028783,0.011465,409272
|
| 34 |
+
math500,trtllm_mha,8,mtp_s15,15,,1,500,500,5,5,0,ok,"1,2,3,4,5",,,500,500,499.795245,0.970217,3292.170089,6.386190,1645406,2.444947,7.293583,7.293583,0.000000,231130
|
| 35 |
+
math500,trtllm_mha,8,mtp_s15,15,,32,500,500,5,5,0,ok,"1,2,3,4,5",,,1500,1500,4855.008946,8.652228,1010.792887,4.438424,4907387,1.856250,7.293583,7.311887,0.008057,686655
|
| 36 |
+
humaneval,trtllm_mha,8,mtp_s15,15,,1,164,164,5,5,0,ok,"1,2,3,4,5",,,164,164,480.467613,1.472814,294.038677,0.900927,141275,2.368501,7.541116,7.541116,0.000000,19796
|
| 37 |
+
humaneval,trtllm_mha,8,mtp_s15,15,,32,164,164,5,5,0,ok,"1,2,3,4,5",,,1148,1148,3917.161422,42.698655,255.505991,5.499563,1000688,1.597063,7.541116,7.621674,0.023832,136276
|
| 38 |
+
mbpp,trtllm_mha,8,mtp_s15,15,,1,257,257,5,5,0,ok,"1,2,3,4,5",,,257,257,445.583765,0.958029,657.470281,1.413204,292957,2.177533,6.397939,6.397939,0.000000,46043
|
| 39 |
+
mbpp,trtllm_mha,8,mtp_s15,15,,32,257,257,5,5,0,ok,"1,2,3,4,5",,,1028,1028,3783.733878,62.936735,308.658226,5.474796,1167609,1.465239,6.397939,6.397951,0.005553,184152
|
| 40 |
+
mt-bench,trtllm_mha,8,mtp_s15,15,,1,80,160,5,5,0,ok,"1,2,3,4,5",,,80,160,337.978110,1.422539,1118.839806,4.712557,378138,1.667979,5.256258,5.256258,0.000000,79620
|
| 41 |
+
mt-bench,trtllm_mha,8,mtp_s15,15,,32,80,160,5,5,0,ok,"1,2,3,4,5",,,560,1120,3100.276145,10.251785,825.233091,5.046098,2558455,1.221890,5.256258,5.243602,0.026024,539673
|
| 42 |
+
gsm8k,trtllm_mha,8,dflash_b4,,4,1,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,529.973579,2.750086,4982.874443,25.817938,2640735,2.590854,3.548232,3.548232,0.000000,764746
|
| 43 |
+
gsm8k,trtllm_mha,8,dflash_b4,,4,32,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,5077.980519,16.739932,511.322717,5.595165,2596508,1.954829,3.548232,3.552208,0.002646,751403
|
| 44 |
+
math500,trtllm_mha,8,dflash_b4,,4,1,500,500,5,5,0,ok,"1,2,3,4,5",,,500,500,551.321433,1.244735,2992.718261,6.748220,1649943,2.697008,3.621767,3.621767,0.000000,457367
|
| 45 |
+
math500,trtllm_mha,8,dflash_b4,,4,32,500,500,5,5,0,ok,"1,2,3,4,5",,,1500,1500,5318.551035,10.917699,929.319749,3.097557,4942635,2.033479,3.621767,3.615034,0.000278,1372522
|
| 46 |
+
humaneval,trtllm_mha,8,dflash_b4,,4,1,164,164,5,5,0,ok,"1,2,3,4,5",,,164,164,543.640692,2.479882,257.803241,1.175757,140150,2.679918,3.696950,3.696950,0.000000,38313
|
| 47 |
+
humaneval,trtllm_mha,8,dflash_b4,,4,32,164,164,5,5,0,ok,"1,2,3,4,5",,,1148,1148,5081.410529,29.000740,208.624984,1.507118,1060084,2.071738,3.696950,3.675101,0.002088,290297
|
| 48 |
+
mbpp,trtllm_mha,8,dflash_b4,,4,1,257,257,5,5,0,ok,"1,2,3,4,5",,,257,257,550.264179,1.811608,534.984026,1.756697,294380,2.689098,3.587588,3.587588,0.000000,82300
|
| 49 |
+
mbpp,trtllm_mha,8,dflash_b4,,4,32,257,257,5,5,0,ok,"1,2,3,4,5",,,1028,1028,5265.730230,28.737314,224.127865,1.861627,1180158,2.039138,3.587588,3.592031,0.001113,329893
|
| 50 |
+
mt-bench,trtllm_mha,8,dflash_b4,,4,1,80,160,5,5,0,ok,"1,2,3,4,5",,,80,160,473.614100,2.823570,774.775195,4.629198,366934,2.337366,3.187625,3.187625,0.000000,118198
|
| 51 |
+
mt-bench,trtllm_mha,8,dflash_b4,,4,32,80,160,5,5,0,ok,"1,2,3,4,5",,,560,1120,4425.336211,24.979476,591.383061,3.704361,2617052,1.744126,3.187625,3.193840,0.002660,839845
|
| 52 |
+
gsm8k,trtllm_mha,8,dflash_b8,,8,1,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,688.975247,0.392468,3765.307512,2.145629,2594203,3.368158,5.737918,5.737918,0.000000,486689
|
| 53 |
+
gsm8k,trtllm_mha,8,dflash_b8,,8,32,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,6158.902318,9.870529,425.115125,4.125581,2618218,2.370942,5.737918,5.736067,0.006153,491935
|
| 54 |
+
math500,trtllm_mha,8,dflash_b8,,8,1,500,500,5,5,0,ok,"1,2,3,4,5",,,500,500,762.557640,0.697669,2156.116490,1.973454,1644162,3.730354,6.002648,6.002648,0.000000,277367
|
| 55 |
+
math500,trtllm_mha,8,dflash_b8,,8,32,500,500,5,5,0,ok,"1,2,3,4,5",,,1500,1500,6910.547547,20.426974,711.452192,2.596787,4916500,2.642159,6.002648,5.997982,0.005109,830565
|
| 56 |
+
humaneval,trtllm_mha,8,dflash_b8,,8,1,164,164,5,5,0,ok,"1,2,3,4,5",,,164,164,752.541701,1.714420,186.686775,0.425482,140489,3.709711,6.292280,6.292280,0.000000,23074
|
| 57 |
+
humaneval,trtllm_mha,8,dflash_b8,,8,32,164,164,5,5,0,ok,"1,2,3,4,5",,,1148,1148,6665.997002,79.178765,159.330876,1.022338,1062057,2.717789,6.292280,6.261022,0.010858,173340
|
| 58 |
+
mbpp,trtllm_mha,8,dflash_b8,,8,1,257,257,5,5,0,ok,"1,2,3,4,5",,,257,257,751.207961,1.162745,401.422233,0.620335,301551,3.671094,5.859880,5.859880,0.000000,52097
|
| 59 |
+
mbpp,trtllm_mha,8,dflash_b8,,8,32,257,257,5,5,0,ok,"1,2,3,4,5",,,1028,1028,6681.357693,82.794608,175.347448,2.882539,1171380,2.587335,5.859880,5.841399,0.004678,203168
|
| 60 |
+
mt-bench,trtllm_mha,8,dflash_b8,,8,1,80,160,5,5,0,ok,"1,2,3,4,5",,,80,160,545.919718,1.078610,673.313558,1.330103,367574,2.694206,4.583530,4.583530,0.000000,86934
|
| 61 |
+
mt-bench,trtllm_mha,8,dflash_b8,,8,32,80,160,5,5,0,ok,"1,2,3,4,5",,,560,1120,4763.179599,22.935557,545.122784,1.479916,2596518,1.877278,4.583530,4.562539,0.003416,614050
|
| 62 |
+
gsm8k,trtllm_mha,8,dflash_b16,,16,1,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,711.312214,0.629131,3647.057609,3.224631,2594195,3.477355,7.695530,7.695530,0.000000,385387
|
| 63 |
+
gsm8k,trtllm_mha,8,dflash_b16,,16,32,1319,1319,5,5,0,ok,"1,2,3,4,5",,,1319,1319,5648.751541,15.871959,465.626848,7.499576,2630146,2.174554,7.695530,7.669634,0.022668,393802
|
| 64 |
+
math500,trtllm_mha,8,dflash_b16,,16,1,500,500,5,5,0,ok,"1,2,3,4,5",,,500,500,831.942883,0.807027,1987.593474,1.928107,1653563,4.069780,8.212200,8.212200,0.000000,208019
|
| 65 |
+
math500,trtllm_mha,8,dflash_b16,,16,32,500,500,5,5,0,ok,"1,2,3,4,5",,,1500,1500,6857.774343,11.793806,720.006772,1.546469,4937653,2.621982,8.212200,8.250632,0.014168,618127
|
| 66 |
+
humaneval,trtllm_mha,8,dflash_b16,,16,1,164,164,5,5,0,ok,"1,2,3,4,5",,,164,164,874.628457,1.713351,168.090176,0.329217,147016,4.311547,9.344168,9.344168,0.000000,16775
|
| 67 |
+
humaneval,trtllm_mha,8,dflash_b16,,16,32,164,164,5,5,0,ok,"1,2,3,4,5",,,1148,1148,6783.744877,39.371020,152.562043,2.393255,1034873,2.765796,9.344168,9.106922,0.022708,119005
|
| 68 |
+
mbpp,trtllm_mha,8,dflash_b16,,16,1,257,257,5,5,0,ok,"1,2,3,4,5",,,257,257,807.812087,1.041081,361.183488,0.465354,291768,3.947714,7.801495,7.801495,0.000000,38256
|
| 69 |
+
mbpp,trtllm_mha,8,dflash_b16,,16,32,257,257,5,5,0,ok,"1,2,3,4,5",,,1028,1028,6344.112863,52.433245,185.962377,2.091507,1179682,2.456738,7.801495,7.820849,0.012002,155067
|
| 70 |
+
mt-bench,trtllm_mha,8,dflash_b16,,16,1,80,160,5,5,0,ok,"1,2,3,4,5",,,80,160,515.340841,0.331810,715.456826,0.460634,368704,2.543294,5.523387,5.523387,0.000000,76747
|
| 71 |
+
mt-bench,trtllm_mha,8,dflash_b16,,16,32,80,160,5,5,0,ok,"1,2,3,4,5",,,560,1120,4015.994589,28.511157,650.353476,6.109586,2611696,1.582795,5.523387,5.534502,0.019500,541217
|
benchmark/results/full-sweep-r5.raw.jsonl
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
benchmark/run_benchmark.py
ADDED
|
@@ -0,0 +1,2406 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import csv
|
| 5 |
+
import io
|
| 6 |
+
import json
|
| 7 |
+
import math
|
| 8 |
+
import os
|
| 9 |
+
import statistics
|
| 10 |
+
import sys
|
| 11 |
+
import time
|
| 12 |
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
| 13 |
+
from dataclasses import dataclass, replace
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
from typing import Any, Optional
|
| 16 |
+
from urllib.request import urlretrieve
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
CACHE_DIR = Path(__file__).resolve().parent / "cache"
|
| 20 |
+
SUPPORTED_WORKLOADS = ("gsm8k", "math500", "humaneval", "mbpp", "mt-bench")
|
| 21 |
+
DEFAULT_WORKLOADS = "gsm8k"
|
| 22 |
+
DEFAULT_TIMEOUT_S = 3600
|
| 23 |
+
DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH = 600
|
| 24 |
+
SERVER_SHUTDOWN_DRAIN_TIMEOUT_S = 30.0
|
| 25 |
+
SERVER_SHUTDOWN_TIMEOUT_S = 120.0
|
| 26 |
+
GSM8K_TEST_URL = "https://raw.githubusercontent.com/openai/grade-school-math/master/grade_school_math/data/test.jsonl"
|
| 27 |
+
MT_BENCH_QUESTION_URL = (
|
| 28 |
+
"https://raw.githubusercontent.com/lm-sys/FastChat/main/"
|
| 29 |
+
"fastchat/llm_judge/data/mt_bench/question.jsonl"
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
@dataclass(frozen=True)
|
| 34 |
+
class SharedServerConfig:
|
| 35 |
+
tp_size: int = 8
|
| 36 |
+
attention_backend: str = "trtllm_mha"
|
| 37 |
+
dtype: str = "bfloat16"
|
| 38 |
+
max_running_requests: int = 32
|
| 39 |
+
cuda_graph_max_bs: int = 32
|
| 40 |
+
mem_fraction_static: Optional[float] = 0.8
|
| 41 |
+
page_size: Optional[int] = None
|
| 42 |
+
mamba_scheduler_strategy: str = "extra_buffer"
|
| 43 |
+
mamba_ssm_dtype: str = "bfloat16"
|
| 44 |
+
linear_attn_backend: str = "flashinfer"
|
| 45 |
+
enable_piecewise_cuda_graph: bool = True
|
| 46 |
+
enable_flashinfer_allreduce_fusion: bool = True
|
| 47 |
+
|
| 48 |
+
def to_args(self) -> list[str]:
|
| 49 |
+
args = [
|
| 50 |
+
"--trust-remote-code",
|
| 51 |
+
"--attention-backend",
|
| 52 |
+
self.attention_backend,
|
| 53 |
+
"--tp-size",
|
| 54 |
+
str(self.tp_size),
|
| 55 |
+
"--dtype",
|
| 56 |
+
self.dtype,
|
| 57 |
+
"--max-running-requests",
|
| 58 |
+
str(self.max_running_requests),
|
| 59 |
+
"--cuda-graph-max-bs-decode",
|
| 60 |
+
str(self.cuda_graph_max_bs),
|
| 61 |
+
"--mamba-scheduler-strategy",
|
| 62 |
+
self.mamba_scheduler_strategy,
|
| 63 |
+
"--mamba-ssm-dtype",
|
| 64 |
+
self.mamba_ssm_dtype,
|
| 65 |
+
"--linear-attn-backend",
|
| 66 |
+
self.linear_attn_backend,
|
| 67 |
+
"--cuda-graph-backend-prefill",
|
| 68 |
+
"tc_piecewise" if self.enable_piecewise_cuda_graph else "disabled",
|
| 69 |
+
]
|
| 70 |
+
if self.enable_flashinfer_allreduce_fusion:
|
| 71 |
+
args.append("--enable-flashinfer-allreduce-fusion")
|
| 72 |
+
if self.mem_fraction_static is not None:
|
| 73 |
+
args.extend(["--mem-fraction-static", str(self.mem_fraction_static)])
|
| 74 |
+
if self.page_size is not None:
|
| 75 |
+
args.extend(["--page-size", str(int(self.page_size))])
|
| 76 |
+
return args
|
| 77 |
+
|
| 78 |
+
def summary_label(self) -> str:
|
| 79 |
+
return (
|
| 80 |
+
f"tp:{self.tp_size},attention:{self.attention_backend},"
|
| 81 |
+
f"dtype:{self.dtype},max_running:{self.max_running_requests},"
|
| 82 |
+
f"cuda_graph_max_bs_decode:{self.cuda_graph_max_bs},"
|
| 83 |
+
f"mem_fraction:{self.mem_fraction_static},page_size:{self.page_size}"
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
BASE_SHARED_SERVER_CONFIG = SharedServerConfig()
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
@dataclass(frozen=True)
|
| 91 |
+
class MTPConfig:
|
| 92 |
+
num_steps: int
|
| 93 |
+
eagle_topk: int = 1
|
| 94 |
+
|
| 95 |
+
@property
|
| 96 |
+
def mode_key(self) -> str:
|
| 97 |
+
return f"mtp_s{self.num_steps}"
|
| 98 |
+
|
| 99 |
+
@property
|
| 100 |
+
def display_name(self) -> str:
|
| 101 |
+
return f"MTP steps={self.num_steps}"
|
| 102 |
+
|
| 103 |
+
@property
|
| 104 |
+
def expect_spec(self) -> bool:
|
| 105 |
+
return True
|
| 106 |
+
|
| 107 |
+
@property
|
| 108 |
+
def num_draft_tokens(self) -> int:
|
| 109 |
+
return self.num_steps + 1
|
| 110 |
+
|
| 111 |
+
def to_args(self) -> list[str]:
|
| 112 |
+
return [
|
| 113 |
+
"--speculative-algorithm",
|
| 114 |
+
"EAGLE",
|
| 115 |
+
"--speculative-num-steps",
|
| 116 |
+
str(self.num_steps),
|
| 117 |
+
"--speculative-eagle-topk",
|
| 118 |
+
str(self.eagle_topk),
|
| 119 |
+
"--speculative-num-draft-tokens",
|
| 120 |
+
str(self.num_draft_tokens),
|
| 121 |
+
]
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
@dataclass(frozen=True)
|
| 125 |
+
class DFlashConfig:
|
| 126 |
+
draft_model: str
|
| 127 |
+
block_size: Optional[int] = None
|
| 128 |
+
draft_attention_backend: str = "fa4"
|
| 129 |
+
|
| 130 |
+
@property
|
| 131 |
+
def mode_key(self) -> str:
|
| 132 |
+
if self.block_size is None:
|
| 133 |
+
return "dflash"
|
| 134 |
+
return f"dflash_b{self.block_size}"
|
| 135 |
+
|
| 136 |
+
@property
|
| 137 |
+
def display_name(self) -> str:
|
| 138 |
+
if self.block_size is None:
|
| 139 |
+
return "DFLASH"
|
| 140 |
+
return f"DFLASH block={self.block_size}"
|
| 141 |
+
|
| 142 |
+
@property
|
| 143 |
+
def expect_spec(self) -> bool:
|
| 144 |
+
return True
|
| 145 |
+
|
| 146 |
+
def to_args(self) -> list[str]:
|
| 147 |
+
args = [
|
| 148 |
+
"--speculative-algorithm",
|
| 149 |
+
"DFLASH",
|
| 150 |
+
"--speculative-draft-model-path",
|
| 151 |
+
self.draft_model,
|
| 152 |
+
"--speculative-draft-attention-backend",
|
| 153 |
+
self.draft_attention_backend,
|
| 154 |
+
]
|
| 155 |
+
if self.block_size is not None:
|
| 156 |
+
args.extend(["--speculative-dflash-block-size", str(int(self.block_size))])
|
| 157 |
+
return args
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
@dataclass(frozen=True)
|
| 161 |
+
class BaselineConfig:
|
| 162 |
+
@property
|
| 163 |
+
def mode_key(self) -> str:
|
| 164 |
+
return "baseline"
|
| 165 |
+
|
| 166 |
+
@property
|
| 167 |
+
def display_name(self) -> str:
|
| 168 |
+
return "Baseline"
|
| 169 |
+
|
| 170 |
+
@property
|
| 171 |
+
def expect_spec(self) -> bool:
|
| 172 |
+
return False
|
| 173 |
+
|
| 174 |
+
def to_args(self) -> list[str]:
|
| 175 |
+
return []
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
@dataclass(frozen=True)
|
| 179 |
+
class ServerDeployment:
|
| 180 |
+
shared_config: SharedServerConfig
|
| 181 |
+
mode_config: BaselineConfig | MTPConfig | DFlashConfig
|
| 182 |
+
|
| 183 |
+
@property
|
| 184 |
+
def mode_key(self) -> str:
|
| 185 |
+
return self.mode_config.mode_key
|
| 186 |
+
|
| 187 |
+
@property
|
| 188 |
+
def display_name(self) -> str:
|
| 189 |
+
return self.mode_config.display_name
|
| 190 |
+
|
| 191 |
+
@property
|
| 192 |
+
def expect_spec(self) -> bool:
|
| 193 |
+
return self.mode_config.expect_spec
|
| 194 |
+
|
| 195 |
+
@property
|
| 196 |
+
def server_args(self) -> list[str]:
|
| 197 |
+
return [*self.shared_config.to_args(), *self.mode_config.to_args()]
|
| 198 |
+
|
| 199 |
+
@property
|
| 200 |
+
def mtp_num_steps(self) -> Optional[int]:
|
| 201 |
+
if isinstance(self.mode_config, MTPConfig):
|
| 202 |
+
return self.mode_config.num_steps
|
| 203 |
+
return None
|
| 204 |
+
|
| 205 |
+
@property
|
| 206 |
+
def dflash_block_size(self) -> Optional[int]:
|
| 207 |
+
if isinstance(self.mode_config, DFlashConfig):
|
| 208 |
+
return self.mode_config.block_size
|
| 209 |
+
return None
|
| 210 |
+
|
| 211 |
+
@property
|
| 212 |
+
def enable_overlap_plan_stream(self) -> bool:
|
| 213 |
+
# MTP on Qwen3.5 uses CUDA HybridLinearAttnBackend, which does not
|
| 214 |
+
# implement update_verify_buffers_to_fill_after_draft for overlap plan
|
| 215 |
+
# streams. DFlash has its own compatible planning path.
|
| 216 |
+
return isinstance(self.mode_config, DFlashConfig)
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
@dataclass(frozen=True)
|
| 220 |
+
class DeploymentSweep:
|
| 221 |
+
include_baseline: bool
|
| 222 |
+
spec_modes: tuple[str, ...]
|
| 223 |
+
mtp_num_steps: tuple[int, ...]
|
| 224 |
+
dflash_draft_model: Optional[str]
|
| 225 |
+
dflash_block_sizes: tuple[Optional[int], ...]
|
| 226 |
+
|
| 227 |
+
@property
|
| 228 |
+
def mode_keys(self) -> list[str]:
|
| 229 |
+
mode_keys: list[str] = []
|
| 230 |
+
for spec_mode in self.spec_modes:
|
| 231 |
+
if spec_mode == "mtp":
|
| 232 |
+
mode_keys.extend(f"mtp_s{int(steps)}" for steps in self.mtp_num_steps)
|
| 233 |
+
elif spec_mode == "dflash":
|
| 234 |
+
for block_size in self.dflash_block_sizes:
|
| 235 |
+
mode_keys.append(
|
| 236 |
+
DFlashConfig("", block_size=block_size).mode_key
|
| 237 |
+
)
|
| 238 |
+
else:
|
| 239 |
+
mode_keys.append(spec_mode)
|
| 240 |
+
return mode_keys
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
@dataclass(frozen=True)
|
| 244 |
+
class SamplingConfig:
|
| 245 |
+
enable_thinking: bool
|
| 246 |
+
max_new_tokens: int
|
| 247 |
+
temperature: float
|
| 248 |
+
top_p: float
|
| 249 |
+
top_k: int
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
@dataclass(frozen=True)
|
| 253 |
+
class BenchmarkMethodologyConfig:
|
| 254 |
+
num_samples: Optional[int]
|
| 255 |
+
min_generation_turns_per_config: int
|
| 256 |
+
min_warmup_generation_turns: int
|
| 257 |
+
runs_per_config: int
|
| 258 |
+
timeout_s: int = DEFAULT_TIMEOUT_S
|
| 259 |
+
server_shutdown_drain_timeout_s: float = SERVER_SHUTDOWN_DRAIN_TIMEOUT_S
|
| 260 |
+
server_shutdown_timeout_s: float = SERVER_SHUTDOWN_TIMEOUT_S
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
@dataclass(frozen=True)
|
| 264 |
+
class SweepConfig:
|
| 265 |
+
target_model: str
|
| 266 |
+
dflash_draft_model: Optional[str]
|
| 267 |
+
workloads: tuple[str, ...]
|
| 268 |
+
concurrencies: tuple[int, ...]
|
| 269 |
+
sampling: SamplingConfig
|
| 270 |
+
methodology: BenchmarkMethodologyConfig
|
| 271 |
+
deployment_sweep: DeploymentSweep
|
| 272 |
+
csv_output: Optional[str]
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
@dataclass(frozen=True)
|
| 276 |
+
class RunKey:
|
| 277 |
+
workload: str
|
| 278 |
+
backend: str
|
| 279 |
+
tp: int
|
| 280 |
+
concurrency: int
|
| 281 |
+
mode: str
|
| 282 |
+
|
| 283 |
+
def metric_key(self) -> tuple[str, int, int, str]:
|
| 284 |
+
return (self.backend, self.tp, self.concurrency, self.mode)
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
@dataclass(frozen=True)
|
| 288 |
+
class BenchmarkPlan:
|
| 289 |
+
measured_samples: list[list[str]]
|
| 290 |
+
warmup_samples: list[list[str]]
|
| 291 |
+
warmdown_samples: list[list[str]]
|
| 292 |
+
|
| 293 |
+
@property
|
| 294 |
+
def measured_sample_count(self) -> int:
|
| 295 |
+
return len(self.measured_samples)
|
| 296 |
+
|
| 297 |
+
@property
|
| 298 |
+
def measured_generation_turn_count(self) -> int:
|
| 299 |
+
return _generation_turn_count(self.measured_samples)
|
| 300 |
+
|
| 301 |
+
@property
|
| 302 |
+
def warmup_generation_turn_count(self) -> int:
|
| 303 |
+
return _generation_turn_count(self.warmup_samples)
|
| 304 |
+
|
| 305 |
+
@property
|
| 306 |
+
def warmdown_generation_turn_count(self) -> int:
|
| 307 |
+
return _generation_turn_count(self.warmdown_samples)
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
@dataclass(frozen=True)
|
| 311 |
+
class BenchmarkJob:
|
| 312 |
+
target_model: str
|
| 313 |
+
workload: str
|
| 314 |
+
deployment: ServerDeployment
|
| 315 |
+
concurrency: int
|
| 316 |
+
run_index: int
|
| 317 |
+
sampling: SamplingConfig
|
| 318 |
+
methodology: BenchmarkMethodologyConfig
|
| 319 |
+
|
| 320 |
+
@property
|
| 321 |
+
def key(self) -> RunKey:
|
| 322 |
+
shared_config = self.deployment.shared_config
|
| 323 |
+
return RunKey(
|
| 324 |
+
workload=self.workload,
|
| 325 |
+
backend=shared_config.attention_backend,
|
| 326 |
+
tp=shared_config.tp_size,
|
| 327 |
+
concurrency=self.concurrency,
|
| 328 |
+
mode=self.deployment.mode_key,
|
| 329 |
+
)
|
| 330 |
+
|
| 331 |
+
@property
|
| 332 |
+
def label(self) -> str:
|
| 333 |
+
key = self.key
|
| 334 |
+
return (
|
| 335 |
+
f"workload={key.workload} backend={key.backend} tp={key.tp} "
|
| 336 |
+
f"conc={key.concurrency} ({self.deployment.display_name})"
|
| 337 |
+
)
|
| 338 |
+
|
| 339 |
+
@property
|
| 340 |
+
def run_label(self) -> str:
|
| 341 |
+
return f"run={self.run_index + 1}/{self.methodology.runs_per_config}"
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
def _parse_int_csv(value: str) -> list[int]:
|
| 345 |
+
return [int(x) for x in value.split(",") if x.strip()]
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
def _parse_optional_int_csv(value: str) -> list[Optional[int]]:
|
| 349 |
+
values: list[Optional[int]] = []
|
| 350 |
+
for raw in value.split(","):
|
| 351 |
+
item = raw.strip().lower()
|
| 352 |
+
if not item:
|
| 353 |
+
continue
|
| 354 |
+
if item in ("default", "none"):
|
| 355 |
+
values.append(None)
|
| 356 |
+
else:
|
| 357 |
+
values.append(int(item))
|
| 358 |
+
return values or [None]
|
| 359 |
+
|
| 360 |
+
|
| 361 |
+
def _parse_str_csv(value: str) -> list[str]:
|
| 362 |
+
return [x.strip().lower() for x in value.split(",") if x.strip()]
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
def _duplicate_values(values: list[str]) -> list[str]:
|
| 366 |
+
seen: set[str] = set()
|
| 367 |
+
duplicates: list[str] = []
|
| 368 |
+
for value in values:
|
| 369 |
+
if value in seen and value not in duplicates:
|
| 370 |
+
duplicates.append(value)
|
| 371 |
+
seen.add(value)
|
| 372 |
+
return duplicates
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
def _shared_server_config_to_payload(config: SharedServerConfig) -> dict[str, Any]:
|
| 376 |
+
return {
|
| 377 |
+
"tp_size": config.tp_size,
|
| 378 |
+
"attention_backend": config.attention_backend,
|
| 379 |
+
"dtype": config.dtype,
|
| 380 |
+
"max_running_requests": config.max_running_requests,
|
| 381 |
+
"cuda_graph_max_bs": config.cuda_graph_max_bs,
|
| 382 |
+
"mem_fraction_static": config.mem_fraction_static,
|
| 383 |
+
"page_size": config.page_size,
|
| 384 |
+
"mamba_scheduler_strategy": config.mamba_scheduler_strategy,
|
| 385 |
+
"mamba_ssm_dtype": config.mamba_ssm_dtype,
|
| 386 |
+
"linear_attn_backend": config.linear_attn_backend,
|
| 387 |
+
"enable_piecewise_cuda_graph": config.enable_piecewise_cuda_graph,
|
| 388 |
+
"enable_flashinfer_allreduce_fusion": (
|
| 389 |
+
config.enable_flashinfer_allreduce_fusion
|
| 390 |
+
),
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
def _shared_server_config_from_payload(payload: dict[str, Any]) -> SharedServerConfig:
|
| 395 |
+
return SharedServerConfig(
|
| 396 |
+
tp_size=int(payload["tp_size"]),
|
| 397 |
+
attention_backend=str(payload["attention_backend"]),
|
| 398 |
+
dtype=str(payload["dtype"]),
|
| 399 |
+
max_running_requests=int(payload["max_running_requests"]),
|
| 400 |
+
cuda_graph_max_bs=int(payload["cuda_graph_max_bs"]),
|
| 401 |
+
mem_fraction_static=payload.get("mem_fraction_static"),
|
| 402 |
+
page_size=payload.get("page_size"),
|
| 403 |
+
mamba_scheduler_strategy=str(payload["mamba_scheduler_strategy"]),
|
| 404 |
+
mamba_ssm_dtype=str(payload["mamba_ssm_dtype"]),
|
| 405 |
+
linear_attn_backend=str(payload["linear_attn_backend"]),
|
| 406 |
+
enable_piecewise_cuda_graph=bool(payload["enable_piecewise_cuda_graph"]),
|
| 407 |
+
enable_flashinfer_allreduce_fusion=bool(
|
| 408 |
+
payload["enable_flashinfer_allreduce_fusion"]
|
| 409 |
+
),
|
| 410 |
+
)
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
def _mode_config_to_payload(
|
| 414 |
+
config: BaselineConfig | MTPConfig | DFlashConfig,
|
| 415 |
+
) -> dict[str, Any]:
|
| 416 |
+
if isinstance(config, BaselineConfig):
|
| 417 |
+
return {"kind": "baseline"}
|
| 418 |
+
if isinstance(config, MTPConfig):
|
| 419 |
+
return {
|
| 420 |
+
"kind": "mtp",
|
| 421 |
+
"num_steps": config.num_steps,
|
| 422 |
+
"eagle_topk": config.eagle_topk,
|
| 423 |
+
}
|
| 424 |
+
if isinstance(config, DFlashConfig):
|
| 425 |
+
return {
|
| 426 |
+
"kind": "dflash",
|
| 427 |
+
"draft_model": config.draft_model,
|
| 428 |
+
"block_size": config.block_size,
|
| 429 |
+
"draft_attention_backend": config.draft_attention_backend,
|
| 430 |
+
}
|
| 431 |
+
raise TypeError(f"Unsupported mode config type: {type(config).__name__}")
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
def _mode_config_from_payload(
|
| 435 |
+
payload: dict[str, Any],
|
| 436 |
+
) -> BaselineConfig | MTPConfig | DFlashConfig:
|
| 437 |
+
kind = payload["kind"]
|
| 438 |
+
if kind == "baseline":
|
| 439 |
+
return BaselineConfig()
|
| 440 |
+
if kind == "mtp":
|
| 441 |
+
return MTPConfig(
|
| 442 |
+
num_steps=int(payload["num_steps"]),
|
| 443 |
+
eagle_topk=int(payload.get("eagle_topk", 1)),
|
| 444 |
+
)
|
| 445 |
+
if kind == "dflash":
|
| 446 |
+
return DFlashConfig(
|
| 447 |
+
draft_model=str(payload["draft_model"]),
|
| 448 |
+
block_size=payload.get("block_size"),
|
| 449 |
+
draft_attention_backend=str(
|
| 450 |
+
payload.get("draft_attention_backend", "fa4")
|
| 451 |
+
),
|
| 452 |
+
)
|
| 453 |
+
raise ValueError(f"Unsupported mode config kind: {kind}")
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
def _deployment_to_payload(deployment: ServerDeployment) -> dict[str, Any]:
|
| 457 |
+
return {
|
| 458 |
+
"shared_config": _shared_server_config_to_payload(deployment.shared_config),
|
| 459 |
+
"mode_config": _mode_config_to_payload(deployment.mode_config),
|
| 460 |
+
}
|
| 461 |
+
|
| 462 |
+
|
| 463 |
+
def _deployment_from_payload(payload: dict[str, Any]) -> ServerDeployment:
|
| 464 |
+
return ServerDeployment(
|
| 465 |
+
shared_config=_shared_server_config_from_payload(payload["shared_config"]),
|
| 466 |
+
mode_config=_mode_config_from_payload(payload["mode_config"]),
|
| 467 |
+
)
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
def _sampling_config_to_payload(config: SamplingConfig) -> dict[str, Any]:
|
| 471 |
+
return {
|
| 472 |
+
"enable_thinking": config.enable_thinking,
|
| 473 |
+
"max_new_tokens": config.max_new_tokens,
|
| 474 |
+
"temperature": config.temperature,
|
| 475 |
+
"top_p": config.top_p,
|
| 476 |
+
"top_k": config.top_k,
|
| 477 |
+
}
|
| 478 |
+
|
| 479 |
+
|
| 480 |
+
def _sampling_config_from_payload(payload: dict[str, Any]) -> SamplingConfig:
|
| 481 |
+
return SamplingConfig(
|
| 482 |
+
enable_thinking=bool(payload["enable_thinking"]),
|
| 483 |
+
max_new_tokens=int(payload["max_new_tokens"]),
|
| 484 |
+
temperature=float(payload["temperature"]),
|
| 485 |
+
top_p=float(payload["top_p"]),
|
| 486 |
+
top_k=int(payload["top_k"]),
|
| 487 |
+
)
|
| 488 |
+
|
| 489 |
+
|
| 490 |
+
def _methodology_to_payload(config: BenchmarkMethodologyConfig) -> dict[str, Any]:
|
| 491 |
+
return {
|
| 492 |
+
"num_samples": config.num_samples,
|
| 493 |
+
"min_generation_turns_per_config": (
|
| 494 |
+
config.min_generation_turns_per_config
|
| 495 |
+
),
|
| 496 |
+
"min_warmup_generation_turns": config.min_warmup_generation_turns,
|
| 497 |
+
"runs_per_config": config.runs_per_config,
|
| 498 |
+
"timeout_s": config.timeout_s,
|
| 499 |
+
"server_shutdown_drain_timeout_s": (
|
| 500 |
+
config.server_shutdown_drain_timeout_s
|
| 501 |
+
),
|
| 502 |
+
"server_shutdown_timeout_s": config.server_shutdown_timeout_s,
|
| 503 |
+
}
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
def _methodology_from_payload(
|
| 507 |
+
payload: dict[str, Any],
|
| 508 |
+
) -> BenchmarkMethodologyConfig:
|
| 509 |
+
return BenchmarkMethodologyConfig(
|
| 510 |
+
num_samples=payload.get("num_samples"),
|
| 511 |
+
min_generation_turns_per_config=int(
|
| 512 |
+
payload["min_generation_turns_per_config"]
|
| 513 |
+
),
|
| 514 |
+
min_warmup_generation_turns=int(payload["min_warmup_generation_turns"]),
|
| 515 |
+
runs_per_config=int(payload["runs_per_config"]),
|
| 516 |
+
timeout_s=int(payload["timeout_s"]),
|
| 517 |
+
server_shutdown_drain_timeout_s=float(
|
| 518 |
+
payload["server_shutdown_drain_timeout_s"]
|
| 519 |
+
),
|
| 520 |
+
server_shutdown_timeout_s=float(payload["server_shutdown_timeout_s"]),
|
| 521 |
+
)
|
| 522 |
+
|
| 523 |
+
|
| 524 |
+
def benchmark_job_to_payload(job: BenchmarkJob) -> dict[str, Any]:
|
| 525 |
+
return {
|
| 526 |
+
"target_model": job.target_model,
|
| 527 |
+
"workload": job.workload,
|
| 528 |
+
"deployment": _deployment_to_payload(job.deployment),
|
| 529 |
+
"concurrency": job.concurrency,
|
| 530 |
+
"run_index": job.run_index,
|
| 531 |
+
"sampling": _sampling_config_to_payload(job.sampling),
|
| 532 |
+
"methodology": _methodology_to_payload(job.methodology),
|
| 533 |
+
}
|
| 534 |
+
|
| 535 |
+
|
| 536 |
+
def benchmark_job_from_payload(payload: dict[str, Any]) -> BenchmarkJob:
|
| 537 |
+
return BenchmarkJob(
|
| 538 |
+
target_model=str(payload["target_model"]),
|
| 539 |
+
workload=str(payload["workload"]),
|
| 540 |
+
deployment=_deployment_from_payload(payload["deployment"]),
|
| 541 |
+
concurrency=int(payload["concurrency"]),
|
| 542 |
+
run_index=int(payload["run_index"]),
|
| 543 |
+
sampling=_sampling_config_from_payload(payload["sampling"]),
|
| 544 |
+
methodology=_methodology_from_payload(payload["methodology"]),
|
| 545 |
+
)
|
| 546 |
+
|
| 547 |
+
|
| 548 |
+
def _parse_workload_selection(value: str) -> list[str]:
|
| 549 |
+
values = _parse_str_csv(value)
|
| 550 |
+
if values == ["all"]:
|
| 551 |
+
return list(SUPPORTED_WORKLOADS)
|
| 552 |
+
unknown = sorted(set(values) - set(SUPPORTED_WORKLOADS))
|
| 553 |
+
if unknown:
|
| 554 |
+
raise ValueError(
|
| 555 |
+
f"Unknown workloads: {','.join(unknown)}. Supported: "
|
| 556 |
+
f"{','.join(SUPPORTED_WORKLOADS)} or all."
|
| 557 |
+
)
|
| 558 |
+
if not values:
|
| 559 |
+
raise ValueError("--workloads must include at least one workload.")
|
| 560 |
+
duplicates = _duplicate_values(values)
|
| 561 |
+
if duplicates:
|
| 562 |
+
raise ValueError(f"Duplicate workloads: {','.join(duplicates)}.")
|
| 563 |
+
return values
|
| 564 |
+
|
| 565 |
+
|
| 566 |
+
def _filter_attention_backends(backends: list[str], *, device_sm: int) -> list[str]:
|
| 567 |
+
if not (80 <= device_sm <= 90):
|
| 568 |
+
backends = [b for b in backends if b != "fa3"]
|
| 569 |
+
if device_sm < 100:
|
| 570 |
+
backends = [b for b in backends if b not in ("fa4", "trtllm_mha")]
|
| 571 |
+
return backends or ["flashinfer"]
|
| 572 |
+
|
| 573 |
+
|
| 574 |
+
def _read_jsonl(path: Path) -> list[dict]:
|
| 575 |
+
with open(path) as f:
|
| 576 |
+
return [json.loads(line) for line in f]
|
| 577 |
+
|
| 578 |
+
|
| 579 |
+
def _download_to_cache(url: str, filename: str) -> Path:
|
| 580 |
+
CACHE_DIR.mkdir(exist_ok=True)
|
| 581 |
+
out_path = CACHE_DIR / filename
|
| 582 |
+
if out_path.exists():
|
| 583 |
+
return out_path
|
| 584 |
+
|
| 585 |
+
tmp_path = out_path.with_name(f"{out_path.name}.{os.getpid()}.tmp")
|
| 586 |
+
print(f"[download] {url}")
|
| 587 |
+
urlretrieve(url, tmp_path)
|
| 588 |
+
os.replace(tmp_path, out_path)
|
| 589 |
+
return out_path
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
def _load_gsm8k_user_prompts() -> list[str]:
|
| 593 |
+
path = _download_to_cache(GSM8K_TEST_URL, "gsm8k_test.jsonl")
|
| 594 |
+
if not path.is_file():
|
| 595 |
+
raise RuntimeError(f"GSM8K data file does not exist: {path}")
|
| 596 |
+
|
| 597 |
+
prompts: list[str] = []
|
| 598 |
+
for row in _read_jsonl(path):
|
| 599 |
+
prompts.append(
|
| 600 |
+
row["question"]
|
| 601 |
+
+ "\nPlease reason step by step, and put your final answer within \\boxed{}."
|
| 602 |
+
)
|
| 603 |
+
return prompts
|
| 604 |
+
|
| 605 |
+
|
| 606 |
+
def _load_math500_user_prompts() -> list[str]:
|
| 607 |
+
rows = _load_hf_dataset_rows("HuggingFaceH4/MATH-500", split="test")
|
| 608 |
+
|
| 609 |
+
prompts: list[str] = []
|
| 610 |
+
for row in rows:
|
| 611 |
+
prompts.append(
|
| 612 |
+
row["problem"]
|
| 613 |
+
+ "\nPlease reason step by step, and put your final answer within \\boxed{}."
|
| 614 |
+
)
|
| 615 |
+
return prompts
|
| 616 |
+
|
| 617 |
+
|
| 618 |
+
def _load_hf_dataset_rows(*load_args, **load_kwargs) -> list[dict]:
|
| 619 |
+
from datasets import load_dataset
|
| 620 |
+
|
| 621 |
+
return list(load_dataset(*load_args, **load_kwargs))
|
| 622 |
+
|
| 623 |
+
|
| 624 |
+
def _load_humaneval_user_prompts() -> list[str]:
|
| 625 |
+
rows = _load_hf_dataset_rows("openai/openai_humaneval", split="test")
|
| 626 |
+
|
| 627 |
+
return [row["prompt"] for row in rows]
|
| 628 |
+
|
| 629 |
+
|
| 630 |
+
def _load_mbpp_user_prompts() -> list[str]:
|
| 631 |
+
rows = _load_hf_dataset_rows(
|
| 632 |
+
"google-research-datasets/mbpp", "sanitized", split="test"
|
| 633 |
+
)
|
| 634 |
+
|
| 635 |
+
return [row["prompt"] for row in rows]
|
| 636 |
+
|
| 637 |
+
|
| 638 |
+
def _load_mt_bench_user_turns() -> list[list[str]]:
|
| 639 |
+
path = _download_to_cache(MT_BENCH_QUESTION_URL, "mt_bench_question.jsonl")
|
| 640 |
+
if not path.is_file():
|
| 641 |
+
raise RuntimeError(f"MT-bench data file does not exist: {path}")
|
| 642 |
+
rows = _read_jsonl(path)
|
| 643 |
+
|
| 644 |
+
prompts: list[list[str]] = []
|
| 645 |
+
for row in rows:
|
| 646 |
+
turns = row.get("turns", row.get("prompt"))
|
| 647 |
+
if not isinstance(turns, list):
|
| 648 |
+
raise RuntimeError(
|
| 649 |
+
"MT-bench rows must contain a list-valued `turns` or `prompt` field."
|
| 650 |
+
)
|
| 651 |
+
turns = [str(turn) for turn in turns[:2]]
|
| 652 |
+
if len(turns) != 2:
|
| 653 |
+
raise RuntimeError(
|
| 654 |
+
f"MT-bench rows must contain exactly two turns; got {len(turns)}."
|
| 655 |
+
)
|
| 656 |
+
prompts.append(turns)
|
| 657 |
+
return prompts
|
| 658 |
+
|
| 659 |
+
|
| 660 |
+
def _load_user_turns(workload: str) -> list[list[str]]:
|
| 661 |
+
if workload == "gsm8k":
|
| 662 |
+
return [[prompt] for prompt in _load_gsm8k_user_prompts()]
|
| 663 |
+
if workload == "math500":
|
| 664 |
+
return [[prompt] for prompt in _load_math500_user_prompts()]
|
| 665 |
+
if workload == "humaneval":
|
| 666 |
+
return [[prompt] for prompt in _load_humaneval_user_prompts()]
|
| 667 |
+
if workload == "mbpp":
|
| 668 |
+
return [[prompt] for prompt in _load_mbpp_user_prompts()]
|
| 669 |
+
if workload == "mt-bench":
|
| 670 |
+
return _load_mt_bench_user_turns()
|
| 671 |
+
raise ValueError(f"Unknown workload: {workload}")
|
| 672 |
+
|
| 673 |
+
|
| 674 |
+
def _flush_cache(
|
| 675 |
+
base_url: str, timeout_s: float = SERVER_SHUTDOWN_DRAIN_TIMEOUT_S
|
| 676 |
+
) -> None:
|
| 677 |
+
import requests
|
| 678 |
+
|
| 679 |
+
try:
|
| 680 |
+
requests.get(
|
| 681 |
+
base_url + "/flush_cache",
|
| 682 |
+
params={"timeout": float(timeout_s)},
|
| 683 |
+
timeout=max(float(timeout_s) + 5.0, 10.0),
|
| 684 |
+
).raise_for_status()
|
| 685 |
+
except Exception as exc:
|
| 686 |
+
raise RuntimeError(
|
| 687 |
+
"Failed to flush cache before the next benchmark phase; "
|
| 688 |
+
"SGLang still had pending requests after waiting for drain."
|
| 689 |
+
) from exc
|
| 690 |
+
|
| 691 |
+
|
| 692 |
+
def _flush_cache_best_effort(base_url: str, timeout_s: float) -> None:
|
| 693 |
+
import requests
|
| 694 |
+
|
| 695 |
+
try:
|
| 696 |
+
requests.get(
|
| 697 |
+
base_url + "/flush_cache",
|
| 698 |
+
params={"timeout": float(timeout_s)},
|
| 699 |
+
timeout=max(float(timeout_s) + 5.0, 10.0),
|
| 700 |
+
).raise_for_status()
|
| 701 |
+
except Exception as exc:
|
| 702 |
+
print(f"[shutdown] /flush_cache failed before server shutdown: {exc}")
|
| 703 |
+
|
| 704 |
+
|
| 705 |
+
def _shutdown_server(proc, base_url: str, *, drain_timeout_s: float, kill_timeout_s: float) -> None:
|
| 706 |
+
from sglang.srt.utils import kill_process_tree
|
| 707 |
+
|
| 708 |
+
if proc.poll() is not None:
|
| 709 |
+
return
|
| 710 |
+
|
| 711 |
+
_flush_cache_best_effort(base_url, drain_timeout_s)
|
| 712 |
+
|
| 713 |
+
if proc.poll() is not None:
|
| 714 |
+
return
|
| 715 |
+
|
| 716 |
+
print(f"[shutdown] sending SIGTERM to server pid={proc.pid}")
|
| 717 |
+
proc.terminate()
|
| 718 |
+
try:
|
| 719 |
+
proc.wait(timeout=float(kill_timeout_s))
|
| 720 |
+
return
|
| 721 |
+
except Exception:
|
| 722 |
+
print(
|
| 723 |
+
f"[shutdown] server pid={proc.pid} did not exit within "
|
| 724 |
+
f"{kill_timeout_s}s; falling back to kill_process_tree."
|
| 725 |
+
)
|
| 726 |
+
|
| 727 |
+
kill_process_tree(proc.pid, wait_timeout=30)
|
| 728 |
+
|
| 729 |
+
|
| 730 |
+
def _send_generate(
|
| 731 |
+
base_url: str,
|
| 732 |
+
text: str,
|
| 733 |
+
*,
|
| 734 |
+
max_new_tokens: int,
|
| 735 |
+
temperature: float,
|
| 736 |
+
top_p: float,
|
| 737 |
+
top_k: int,
|
| 738 |
+
timeout_s: int,
|
| 739 |
+
) -> dict:
|
| 740 |
+
import requests
|
| 741 |
+
|
| 742 |
+
sampling_params: dict = {
|
| 743 |
+
"temperature": float(temperature),
|
| 744 |
+
"top_p": float(top_p),
|
| 745 |
+
"top_k": int(top_k),
|
| 746 |
+
"max_new_tokens": int(max_new_tokens),
|
| 747 |
+
}
|
| 748 |
+
resp = requests.post(
|
| 749 |
+
base_url + "/generate",
|
| 750 |
+
json={
|
| 751 |
+
"text": text,
|
| 752 |
+
"sampling_params": sampling_params,
|
| 753 |
+
},
|
| 754 |
+
timeout=int(timeout_s),
|
| 755 |
+
)
|
| 756 |
+
resp.raise_for_status()
|
| 757 |
+
out = resp.json()
|
| 758 |
+
if isinstance(out, list):
|
| 759 |
+
raise RuntimeError(
|
| 760 |
+
"Expected an object response for single /generate, but got "
|
| 761 |
+
f"type={type(out).__name__}."
|
| 762 |
+
)
|
| 763 |
+
return out
|
| 764 |
+
|
| 765 |
+
|
| 766 |
+
@dataclass(frozen=True)
|
| 767 |
+
class BenchMetrics:
|
| 768 |
+
sample_count: int
|
| 769 |
+
generation_turn_count: int
|
| 770 |
+
latency_s: float
|
| 771 |
+
output_tokens: int
|
| 772 |
+
output_toks_per_s: float
|
| 773 |
+
spec_accept_length: Optional[float]
|
| 774 |
+
spec_verify_ct_sum: int
|
| 775 |
+
|
| 776 |
+
|
| 777 |
+
@dataclass(frozen=True)
|
| 778 |
+
class JobResult:
|
| 779 |
+
key: RunKey
|
| 780 |
+
deployment: ServerDeployment
|
| 781 |
+
source_sample_count: int
|
| 782 |
+
source_generation_turn_count: int
|
| 783 |
+
warmup_generation_turn_count: int
|
| 784 |
+
warmdown_generation_turn_count: int
|
| 785 |
+
run_index: int
|
| 786 |
+
metrics: BenchMetrics
|
| 787 |
+
|
| 788 |
+
|
| 789 |
+
@dataclass(frozen=True)
|
| 790 |
+
class JobFailure:
|
| 791 |
+
key: RunKey
|
| 792 |
+
deployment: ServerDeployment
|
| 793 |
+
run_index: int
|
| 794 |
+
error_type: str
|
| 795 |
+
error_message: str
|
| 796 |
+
|
| 797 |
+
|
| 798 |
+
@dataclass(frozen=True)
|
| 799 |
+
class ConfigResult:
|
| 800 |
+
key: RunKey
|
| 801 |
+
deployment: ServerDeployment
|
| 802 |
+
source_sample_count: Optional[int]
|
| 803 |
+
source_generation_turn_count: Optional[int]
|
| 804 |
+
warmup_generation_turn_count: Optional[int]
|
| 805 |
+
warmdown_generation_turn_count: Optional[int]
|
| 806 |
+
metrics: Optional[BenchMetrics]
|
| 807 |
+
repeat_metrics: tuple[BenchMetrics, ...]
|
| 808 |
+
successful_run_indices: tuple[int, ...]
|
| 809 |
+
failures: tuple[JobFailure, ...]
|
| 810 |
+
|
| 811 |
+
@property
|
| 812 |
+
def run_count(self) -> int:
|
| 813 |
+
return self.successful_run_count + self.failed_run_count
|
| 814 |
+
|
| 815 |
+
@property
|
| 816 |
+
def successful_run_count(self) -> int:
|
| 817 |
+
return len(self.repeat_metrics)
|
| 818 |
+
|
| 819 |
+
@property
|
| 820 |
+
def failed_run_count(self) -> int:
|
| 821 |
+
return len(self.failures)
|
| 822 |
+
|
| 823 |
+
@property
|
| 824 |
+
def status(self) -> str:
|
| 825 |
+
if self.failed_run_count == 0:
|
| 826 |
+
return "ok"
|
| 827 |
+
if self.successful_run_count == 0:
|
| 828 |
+
return "failed"
|
| 829 |
+
return "partial_failed"
|
| 830 |
+
|
| 831 |
+
|
| 832 |
+
def _run_key_to_payload(key: RunKey) -> dict[str, Any]:
|
| 833 |
+
return {
|
| 834 |
+
"workload": key.workload,
|
| 835 |
+
"backend": key.backend,
|
| 836 |
+
"tp": key.tp,
|
| 837 |
+
"concurrency": key.concurrency,
|
| 838 |
+
"mode": key.mode,
|
| 839 |
+
}
|
| 840 |
+
|
| 841 |
+
|
| 842 |
+
def _run_key_from_payload(payload: dict[str, Any]) -> RunKey:
|
| 843 |
+
return RunKey(
|
| 844 |
+
workload=str(payload["workload"]),
|
| 845 |
+
backend=str(payload["backend"]),
|
| 846 |
+
tp=int(payload["tp"]),
|
| 847 |
+
concurrency=int(payload["concurrency"]),
|
| 848 |
+
mode=str(payload["mode"]),
|
| 849 |
+
)
|
| 850 |
+
|
| 851 |
+
|
| 852 |
+
def _bench_metrics_to_payload(metrics: BenchMetrics) -> dict[str, Any]:
|
| 853 |
+
return {
|
| 854 |
+
"sample_count": metrics.sample_count,
|
| 855 |
+
"generation_turn_count": metrics.generation_turn_count,
|
| 856 |
+
"latency_s": metrics.latency_s,
|
| 857 |
+
"output_tokens": metrics.output_tokens,
|
| 858 |
+
"output_toks_per_s": metrics.output_toks_per_s,
|
| 859 |
+
"spec_accept_length": metrics.spec_accept_length,
|
| 860 |
+
"spec_verify_ct_sum": metrics.spec_verify_ct_sum,
|
| 861 |
+
}
|
| 862 |
+
|
| 863 |
+
|
| 864 |
+
def _bench_metrics_from_payload(payload: dict[str, Any]) -> BenchMetrics:
|
| 865 |
+
return BenchMetrics(
|
| 866 |
+
sample_count=int(payload["sample_count"]),
|
| 867 |
+
generation_turn_count=int(payload["generation_turn_count"]),
|
| 868 |
+
latency_s=float(payload["latency_s"]),
|
| 869 |
+
output_tokens=int(payload["output_tokens"]),
|
| 870 |
+
output_toks_per_s=float(payload["output_toks_per_s"]),
|
| 871 |
+
spec_accept_length=payload.get("spec_accept_length"),
|
| 872 |
+
spec_verify_ct_sum=int(payload["spec_verify_ct_sum"]),
|
| 873 |
+
)
|
| 874 |
+
|
| 875 |
+
|
| 876 |
+
def job_outcome_to_payload(result: JobResult | JobFailure) -> dict[str, Any]:
|
| 877 |
+
if isinstance(result, JobFailure):
|
| 878 |
+
return {
|
| 879 |
+
"kind": "failure",
|
| 880 |
+
"key": _run_key_to_payload(result.key),
|
| 881 |
+
"deployment": _deployment_to_payload(result.deployment),
|
| 882 |
+
"run_index": result.run_index,
|
| 883 |
+
"error_type": result.error_type,
|
| 884 |
+
"error_message": result.error_message,
|
| 885 |
+
}
|
| 886 |
+
return {
|
| 887 |
+
"kind": "result",
|
| 888 |
+
"key": _run_key_to_payload(result.key),
|
| 889 |
+
"deployment": _deployment_to_payload(result.deployment),
|
| 890 |
+
"source_sample_count": result.source_sample_count,
|
| 891 |
+
"source_generation_turn_count": result.source_generation_turn_count,
|
| 892 |
+
"warmup_generation_turn_count": result.warmup_generation_turn_count,
|
| 893 |
+
"warmdown_generation_turn_count": result.warmdown_generation_turn_count,
|
| 894 |
+
"run_index": result.run_index,
|
| 895 |
+
"metrics": _bench_metrics_to_payload(result.metrics),
|
| 896 |
+
}
|
| 897 |
+
|
| 898 |
+
|
| 899 |
+
def job_outcome_from_payload(payload: dict[str, Any]) -> JobResult | JobFailure:
|
| 900 |
+
kind = payload["kind"]
|
| 901 |
+
if kind == "failure":
|
| 902 |
+
return JobFailure(
|
| 903 |
+
key=_run_key_from_payload(payload["key"]),
|
| 904 |
+
deployment=_deployment_from_payload(payload["deployment"]),
|
| 905 |
+
run_index=int(payload["run_index"]),
|
| 906 |
+
error_type=str(payload["error_type"]),
|
| 907 |
+
error_message=str(payload["error_message"]),
|
| 908 |
+
)
|
| 909 |
+
if kind == "result":
|
| 910 |
+
return JobResult(
|
| 911 |
+
key=_run_key_from_payload(payload["key"]),
|
| 912 |
+
deployment=_deployment_from_payload(payload["deployment"]),
|
| 913 |
+
source_sample_count=int(payload["source_sample_count"]),
|
| 914 |
+
source_generation_turn_count=int(
|
| 915 |
+
payload["source_generation_turn_count"]
|
| 916 |
+
),
|
| 917 |
+
warmup_generation_turn_count=int(
|
| 918 |
+
payload["warmup_generation_turn_count"]
|
| 919 |
+
),
|
| 920 |
+
warmdown_generation_turn_count=int(
|
| 921 |
+
payload["warmdown_generation_turn_count"]
|
| 922 |
+
),
|
| 923 |
+
run_index=int(payload["run_index"]),
|
| 924 |
+
metrics=_bench_metrics_from_payload(payload["metrics"]),
|
| 925 |
+
)
|
| 926 |
+
raise ValueError(f"Unsupported job outcome kind: {kind}")
|
| 927 |
+
|
| 928 |
+
|
| 929 |
+
@dataclass(frozen=True)
|
| 930 |
+
class SampleMetrics:
|
| 931 |
+
generation_turn_count: int
|
| 932 |
+
output_tokens: int
|
| 933 |
+
spec_verify_ct_sum: int
|
| 934 |
+
spec_accept_lengths: tuple[float, ...]
|
| 935 |
+
|
| 936 |
+
|
| 937 |
+
def _extract_generated_text(out: dict) -> str:
|
| 938 |
+
text = out.get("text")
|
| 939 |
+
if isinstance(text, str):
|
| 940 |
+
return text
|
| 941 |
+
if isinstance(text, list) and len(text) == 1 and isinstance(text[0], str):
|
| 942 |
+
return text[0]
|
| 943 |
+
raise RuntimeError(
|
| 944 |
+
"Expected /generate response to include generated text in `text`; "
|
| 945 |
+
f"got keys={sorted(out.keys())}."
|
| 946 |
+
)
|
| 947 |
+
|
| 948 |
+
|
| 949 |
+
def _extract_generate_stats(out: dict) -> tuple[int, int, Optional[float]]:
|
| 950 |
+
meta = out.get("meta_info", {}) or {}
|
| 951 |
+
output_tokens = int(meta.get("completion_tokens", 0))
|
| 952 |
+
spec_verify_ct = int(meta.get("spec_verify_ct", 0))
|
| 953 |
+
spec_accept_length = None
|
| 954 |
+
if "spec_accept_length" in meta:
|
| 955 |
+
try:
|
| 956 |
+
spec_accept_length = float(meta["spec_accept_length"])
|
| 957 |
+
except (TypeError, ValueError):
|
| 958 |
+
pass
|
| 959 |
+
return output_tokens, spec_verify_ct, spec_accept_length
|
| 960 |
+
|
| 961 |
+
|
| 962 |
+
def _run_sample(
|
| 963 |
+
base_url: str,
|
| 964 |
+
*,
|
| 965 |
+
turns: list[str],
|
| 966 |
+
tokenizer,
|
| 967 |
+
sampling: SamplingConfig,
|
| 968 |
+
timeout_s: int,
|
| 969 |
+
) -> SampleMetrics:
|
| 970 |
+
messages: list[dict[str, str]] = []
|
| 971 |
+
total_tokens = 0
|
| 972 |
+
spec_verify_ct_sum = 0
|
| 973 |
+
turn_accept_lengths: list[float] = []
|
| 974 |
+
|
| 975 |
+
for turn_idx, user_content in enumerate(turns):
|
| 976 |
+
messages.append({"role": "user", "content": user_content})
|
| 977 |
+
prompt = tokenizer.apply_chat_template(
|
| 978 |
+
messages,
|
| 979 |
+
tokenize=False,
|
| 980 |
+
add_generation_prompt=True,
|
| 981 |
+
enable_thinking=bool(sampling.enable_thinking),
|
| 982 |
+
)
|
| 983 |
+
out = _send_generate(
|
| 984 |
+
base_url=base_url,
|
| 985 |
+
text=prompt,
|
| 986 |
+
max_new_tokens=sampling.max_new_tokens,
|
| 987 |
+
temperature=sampling.temperature,
|
| 988 |
+
top_p=sampling.top_p,
|
| 989 |
+
top_k=sampling.top_k,
|
| 990 |
+
timeout_s=timeout_s,
|
| 991 |
+
)
|
| 992 |
+
output_tokens, spec_verify_ct, turn_accept_length = _extract_generate_stats(out)
|
| 993 |
+
total_tokens += output_tokens
|
| 994 |
+
spec_verify_ct_sum += spec_verify_ct
|
| 995 |
+
if spec_verify_ct > 0:
|
| 996 |
+
turn_accept_lengths.append(float(output_tokens) / float(spec_verify_ct))
|
| 997 |
+
elif turn_accept_length is not None:
|
| 998 |
+
turn_accept_lengths.append(turn_accept_length)
|
| 999 |
+
|
| 1000 |
+
if turn_idx + 1 < len(turns):
|
| 1001 |
+
messages.append({"role": "assistant", "content": _extract_generated_text(out)})
|
| 1002 |
+
|
| 1003 |
+
return SampleMetrics(
|
| 1004 |
+
generation_turn_count=len(turns),
|
| 1005 |
+
output_tokens=int(total_tokens),
|
| 1006 |
+
spec_verify_ct_sum=int(spec_verify_ct_sum),
|
| 1007 |
+
spec_accept_lengths=tuple(turn_accept_lengths),
|
| 1008 |
+
)
|
| 1009 |
+
|
| 1010 |
+
|
| 1011 |
+
def _run_unmeasured_requests(
|
| 1012 |
+
base_url: str,
|
| 1013 |
+
*,
|
| 1014 |
+
samples: list[list[str]],
|
| 1015 |
+
tokenizer,
|
| 1016 |
+
sampling: SamplingConfig,
|
| 1017 |
+
concurrency: int,
|
| 1018 |
+
timeout_s: int,
|
| 1019 |
+
) -> None:
|
| 1020 |
+
if not samples:
|
| 1021 |
+
return
|
| 1022 |
+
|
| 1023 |
+
with ThreadPoolExecutor(max_workers=int(concurrency)) as pool:
|
| 1024 |
+
futures = [
|
| 1025 |
+
pool.submit(
|
| 1026 |
+
_run_sample,
|
| 1027 |
+
base_url=base_url,
|
| 1028 |
+
turns=turns,
|
| 1029 |
+
tokenizer=tokenizer,
|
| 1030 |
+
sampling=sampling,
|
| 1031 |
+
timeout_s=timeout_s,
|
| 1032 |
+
)
|
| 1033 |
+
for turns in samples
|
| 1034 |
+
]
|
| 1035 |
+
for fut in as_completed(futures):
|
| 1036 |
+
fut.result()
|
| 1037 |
+
|
| 1038 |
+
|
| 1039 |
+
def _take_samples(samples: list[list[str]], *, start: int, count: int) -> list[list[str]]:
|
| 1040 |
+
if count <= 0:
|
| 1041 |
+
return []
|
| 1042 |
+
if not samples:
|
| 1043 |
+
raise RuntimeError("Cannot take benchmark samples from an empty workload.")
|
| 1044 |
+
return [samples[(start + i) % len(samples)] for i in range(count)]
|
| 1045 |
+
|
| 1046 |
+
|
| 1047 |
+
def _generation_turn_count(samples: list[list[str]]) -> int:
|
| 1048 |
+
return sum(len(turns) for turns in samples)
|
| 1049 |
+
|
| 1050 |
+
|
| 1051 |
+
def _take_samples_for_min_generation_turns(
|
| 1052 |
+
samples: list[list[str]], *, start: int, min_generation_turns: int
|
| 1053 |
+
) -> list[list[str]]:
|
| 1054 |
+
if min_generation_turns <= 0:
|
| 1055 |
+
return []
|
| 1056 |
+
if not samples:
|
| 1057 |
+
raise RuntimeError("Cannot take benchmark samples from an empty workload.")
|
| 1058 |
+
|
| 1059 |
+
out: list[list[str]] = []
|
| 1060 |
+
generation_turns = 0
|
| 1061 |
+
idx = 0
|
| 1062 |
+
while generation_turns < int(min_generation_turns):
|
| 1063 |
+
sample = samples[(start + idx) % len(samples)]
|
| 1064 |
+
out.append(sample)
|
| 1065 |
+
generation_turns += len(sample)
|
| 1066 |
+
idx += 1
|
| 1067 |
+
return out
|
| 1068 |
+
|
| 1069 |
+
|
| 1070 |
+
def _build_measured_samples(
|
| 1071 |
+
samples: list[list[str]], *, num_samples: Optional[int], min_generation_turns: int
|
| 1072 |
+
) -> list[list[str]]:
|
| 1073 |
+
if not samples:
|
| 1074 |
+
raise RuntimeError("Cannot build measured samples from an empty workload.")
|
| 1075 |
+
if num_samples is not None:
|
| 1076 |
+
if num_samples <= 0:
|
| 1077 |
+
raise RuntimeError(f"--num-samples must be > 0, got {num_samples}.")
|
| 1078 |
+
return _take_samples(samples, start=0, count=int(num_samples))
|
| 1079 |
+
if min_generation_turns < 0:
|
| 1080 |
+
raise RuntimeError(
|
| 1081 |
+
"--min-generation-turns-per-config must be >= 0, "
|
| 1082 |
+
f"got {min_generation_turns}."
|
| 1083 |
+
)
|
| 1084 |
+
source_generation_turns = _generation_turn_count(samples)
|
| 1085 |
+
if source_generation_turns <= 0:
|
| 1086 |
+
raise RuntimeError("Cannot build measured samples with zero generation turns.")
|
| 1087 |
+
repeats = max(1, math.ceil(int(min_generation_turns) / source_generation_turns))
|
| 1088 |
+
return samples * repeats
|
| 1089 |
+
|
| 1090 |
+
|
| 1091 |
+
def _build_measured_samples_for_concurrency(
|
| 1092 |
+
samples: list[list[str]],
|
| 1093 |
+
*,
|
| 1094 |
+
num_samples: Optional[int],
|
| 1095 |
+
min_generation_turns: int,
|
| 1096 |
+
concurrency: int,
|
| 1097 |
+
) -> list[list[str]]:
|
| 1098 |
+
# Concurrency 1 is the stable accept-length pass; use one full workload by
|
| 1099 |
+
# default instead of cache-favorable repeated copies.
|
| 1100 |
+
if num_samples is None and int(concurrency) == 1:
|
| 1101 |
+
return samples
|
| 1102 |
+
return _build_measured_samples(
|
| 1103 |
+
samples,
|
| 1104 |
+
num_samples=num_samples,
|
| 1105 |
+
min_generation_turns=min_generation_turns,
|
| 1106 |
+
)
|
| 1107 |
+
|
| 1108 |
+
|
| 1109 |
+
def _build_benchmark_plan(
|
| 1110 |
+
samples: list[list[str]],
|
| 1111 |
+
*,
|
| 1112 |
+
concurrency: int,
|
| 1113 |
+
methodology: BenchmarkMethodologyConfig,
|
| 1114 |
+
) -> BenchmarkPlan:
|
| 1115 |
+
measured_samples = _build_measured_samples_for_concurrency(
|
| 1116 |
+
samples,
|
| 1117 |
+
num_samples=methodology.num_samples,
|
| 1118 |
+
min_generation_turns=int(methodology.min_generation_turns_per_config),
|
| 1119 |
+
concurrency=int(concurrency),
|
| 1120 |
+
)
|
| 1121 |
+
warmup_min_generation_turns = max(
|
| 1122 |
+
int(methodology.min_warmup_generation_turns), 2 * int(concurrency)
|
| 1123 |
+
)
|
| 1124 |
+
warmup_samples = _take_samples_for_min_generation_turns(
|
| 1125 |
+
measured_samples,
|
| 1126 |
+
start=0,
|
| 1127 |
+
min_generation_turns=warmup_min_generation_turns,
|
| 1128 |
+
)
|
| 1129 |
+
warmdown_samples = _take_samples(
|
| 1130 |
+
measured_samples,
|
| 1131 |
+
start=len(warmup_samples),
|
| 1132 |
+
count=int(concurrency),
|
| 1133 |
+
)
|
| 1134 |
+
return BenchmarkPlan(
|
| 1135 |
+
measured_samples=measured_samples,
|
| 1136 |
+
warmup_samples=warmup_samples,
|
| 1137 |
+
warmdown_samples=warmdown_samples,
|
| 1138 |
+
)
|
| 1139 |
+
|
| 1140 |
+
|
| 1141 |
+
def _run_requests(
|
| 1142 |
+
base_url: str,
|
| 1143 |
+
*,
|
| 1144 |
+
samples: list[list[str]],
|
| 1145 |
+
warmdown_samples: list[list[str]],
|
| 1146 |
+
tokenizer,
|
| 1147 |
+
sampling: SamplingConfig,
|
| 1148 |
+
concurrency: int,
|
| 1149 |
+
timeout_s: int,
|
| 1150 |
+
expect_spec: bool,
|
| 1151 |
+
) -> BenchMetrics:
|
| 1152 |
+
start = time.perf_counter()
|
| 1153 |
+
total_tokens = 0
|
| 1154 |
+
spec_verify_ct_sum = 0
|
| 1155 |
+
generation_turn_count = 0
|
| 1156 |
+
turn_accept_lengths: list[float] = []
|
| 1157 |
+
measured_completed = 0
|
| 1158 |
+
latency: Optional[float] = None
|
| 1159 |
+
|
| 1160 |
+
with ThreadPoolExecutor(max_workers=int(concurrency)) as pool:
|
| 1161 |
+
measured_futures = [
|
| 1162 |
+
pool.submit(
|
| 1163 |
+
_run_sample,
|
| 1164 |
+
base_url=base_url,
|
| 1165 |
+
turns=turns,
|
| 1166 |
+
tokenizer=tokenizer,
|
| 1167 |
+
sampling=sampling,
|
| 1168 |
+
timeout_s=timeout_s,
|
| 1169 |
+
)
|
| 1170 |
+
for turns in samples
|
| 1171 |
+
]
|
| 1172 |
+
measured_future_set = set(measured_futures)
|
| 1173 |
+
# Queue warmdown behind measured work so the server does not immediately
|
| 1174 |
+
# drain to idle as the measured tail completes. These futures are waited
|
| 1175 |
+
# on for correctness, but excluded from timing and metrics.
|
| 1176 |
+
warmdown_futures = [
|
| 1177 |
+
pool.submit(
|
| 1178 |
+
_run_sample,
|
| 1179 |
+
base_url=base_url,
|
| 1180 |
+
turns=turns,
|
| 1181 |
+
tokenizer=tokenizer,
|
| 1182 |
+
sampling=sampling,
|
| 1183 |
+
timeout_s=timeout_s,
|
| 1184 |
+
)
|
| 1185 |
+
for turns in warmdown_samples
|
| 1186 |
+
]
|
| 1187 |
+
consumed_warmdown_futures = set()
|
| 1188 |
+
|
| 1189 |
+
for fut in as_completed([*measured_futures, *warmdown_futures]):
|
| 1190 |
+
if fut in measured_future_set:
|
| 1191 |
+
sample_metrics = fut.result()
|
| 1192 |
+
total_tokens += sample_metrics.output_tokens
|
| 1193 |
+
spec_verify_ct_sum += sample_metrics.spec_verify_ct_sum
|
| 1194 |
+
generation_turn_count += sample_metrics.generation_turn_count
|
| 1195 |
+
turn_accept_lengths.extend(sample_metrics.spec_accept_lengths)
|
| 1196 |
+
measured_completed += 1
|
| 1197 |
+
if measured_completed == len(measured_futures):
|
| 1198 |
+
latency = time.perf_counter() - start
|
| 1199 |
+
break
|
| 1200 |
+
else:
|
| 1201 |
+
consumed_warmdown_futures.add(fut)
|
| 1202 |
+
fut.result()
|
| 1203 |
+
|
| 1204 |
+
for fut in warmdown_futures:
|
| 1205 |
+
if fut not in consumed_warmdown_futures:
|
| 1206 |
+
fut.result()
|
| 1207 |
+
|
| 1208 |
+
if latency is None:
|
| 1209 |
+
latency = time.perf_counter() - start
|
| 1210 |
+
toks_per_s = total_tokens / max(latency, 1e-6)
|
| 1211 |
+
|
| 1212 |
+
if expect_spec and spec_verify_ct_sum <= 0:
|
| 1213 |
+
raise RuntimeError(
|
| 1214 |
+
"Speculative decoding sanity check failed: did not observe any "
|
| 1215 |
+
"`spec_verify_ct` in responses (speculative decoding may not have been enabled)."
|
| 1216 |
+
)
|
| 1217 |
+
|
| 1218 |
+
spec_accept_length = (
|
| 1219 |
+
float(statistics.mean(turn_accept_lengths))
|
| 1220 |
+
if turn_accept_lengths
|
| 1221 |
+
else None
|
| 1222 |
+
)
|
| 1223 |
+
|
| 1224 |
+
return BenchMetrics(
|
| 1225 |
+
sample_count=len(samples),
|
| 1226 |
+
generation_turn_count=int(generation_turn_count),
|
| 1227 |
+
latency_s=float(latency),
|
| 1228 |
+
output_tokens=int(total_tokens),
|
| 1229 |
+
output_toks_per_s=float(toks_per_s),
|
| 1230 |
+
spec_accept_length=spec_accept_length,
|
| 1231 |
+
spec_verify_ct_sum=int(spec_verify_ct_sum),
|
| 1232 |
+
)
|
| 1233 |
+
|
| 1234 |
+
|
| 1235 |
+
def _format_table(
|
| 1236 |
+
*,
|
| 1237 |
+
tp_sizes: list[int],
|
| 1238 |
+
concurrencies: list[int],
|
| 1239 |
+
values: dict[tuple[int, int], Optional[float]],
|
| 1240 |
+
float_fmt: str,
|
| 1241 |
+
) -> str:
|
| 1242 |
+
header = ["tp\\conc"] + [str(c) for c in concurrencies]
|
| 1243 |
+
rows: list[list[str]] = [header]
|
| 1244 |
+
for tp in tp_sizes:
|
| 1245 |
+
row = [str(tp)]
|
| 1246 |
+
for c in concurrencies:
|
| 1247 |
+
v = values.get((tp, c), None)
|
| 1248 |
+
row.append("N/A" if v is None else format(v, float_fmt))
|
| 1249 |
+
rows.append(row)
|
| 1250 |
+
|
| 1251 |
+
col_widths = [
|
| 1252 |
+
max(len(row[col_idx]) for row in rows) for col_idx in range(len(rows[0]))
|
| 1253 |
+
]
|
| 1254 |
+
|
| 1255 |
+
lines: list[str] = []
|
| 1256 |
+
lines.append(" ".join(cell.rjust(col_widths[i]) for i, cell in enumerate(rows[0])))
|
| 1257 |
+
lines.append(" ".join("-" * w for w in col_widths))
|
| 1258 |
+
for row in rows[1:]:
|
| 1259 |
+
lines.append(" ".join(cell.rjust(col_widths[i]) for i, cell in enumerate(row)))
|
| 1260 |
+
return "\n".join(lines)
|
| 1261 |
+
|
| 1262 |
+
|
| 1263 |
+
def _build_shared_server_configs(
|
| 1264 |
+
*, device_sm: int, visible_gpus: int, max_concurrency: int
|
| 1265 |
+
) -> list[SharedServerConfig]:
|
| 1266 |
+
attention_backends = _filter_attention_backends(
|
| 1267 |
+
[BASE_SHARED_SERVER_CONFIG.attention_backend], device_sm=device_sm
|
| 1268 |
+
)
|
| 1269 |
+
scheduler_capacity = max(
|
| 1270 |
+
BASE_SHARED_SERVER_CONFIG.max_running_requests,
|
| 1271 |
+
int(max_concurrency),
|
| 1272 |
+
)
|
| 1273 |
+
configs = [
|
| 1274 |
+
replace(
|
| 1275 |
+
BASE_SHARED_SERVER_CONFIG,
|
| 1276 |
+
attention_backend=backend,
|
| 1277 |
+
max_running_requests=scheduler_capacity,
|
| 1278 |
+
cuda_graph_max_bs=max(
|
| 1279 |
+
BASE_SHARED_SERVER_CONFIG.cuda_graph_max_bs,
|
| 1280 |
+
scheduler_capacity,
|
| 1281 |
+
),
|
| 1282 |
+
)
|
| 1283 |
+
for backend in attention_backends
|
| 1284 |
+
]
|
| 1285 |
+
runnable_configs = [
|
| 1286 |
+
config for config in configs if 1 <= config.tp_size <= visible_gpus
|
| 1287 |
+
]
|
| 1288 |
+
if not runnable_configs:
|
| 1289 |
+
raise RuntimeError(
|
| 1290 |
+
f"No shared server configs are runnable with visible_gpus={visible_gpus}. "
|
| 1291 |
+
"Set CUDA_VISIBLE_DEVICES accordingly."
|
| 1292 |
+
)
|
| 1293 |
+
return runnable_configs
|
| 1294 |
+
|
| 1295 |
+
|
| 1296 |
+
def _build_deployments(
|
| 1297 |
+
shared_config: SharedServerConfig, sweep: DeploymentSweep
|
| 1298 |
+
) -> list[ServerDeployment]:
|
| 1299 |
+
deployments: list[ServerDeployment] = []
|
| 1300 |
+
if sweep.include_baseline:
|
| 1301 |
+
deployments.append(
|
| 1302 |
+
ServerDeployment(
|
| 1303 |
+
shared_config=shared_config,
|
| 1304 |
+
mode_config=BaselineConfig(),
|
| 1305 |
+
)
|
| 1306 |
+
)
|
| 1307 |
+
|
| 1308 |
+
for spec_mode in sweep.spec_modes:
|
| 1309 |
+
if spec_mode == "mtp":
|
| 1310 |
+
for mtp_num_steps in sweep.mtp_num_steps:
|
| 1311 |
+
mtp_config = MTPConfig(num_steps=int(mtp_num_steps))
|
| 1312 |
+
deployments.append(
|
| 1313 |
+
ServerDeployment(
|
| 1314 |
+
shared_config=shared_config,
|
| 1315 |
+
mode_config=mtp_config,
|
| 1316 |
+
)
|
| 1317 |
+
)
|
| 1318 |
+
elif spec_mode == "dflash":
|
| 1319 |
+
if sweep.dflash_draft_model is None:
|
| 1320 |
+
raise RuntimeError("DFlash deployment requires a draft model.")
|
| 1321 |
+
for block_size in sweep.dflash_block_sizes:
|
| 1322 |
+
dflash_config = DFlashConfig(
|
| 1323 |
+
draft_model=sweep.dflash_draft_model,
|
| 1324 |
+
block_size=block_size,
|
| 1325 |
+
)
|
| 1326 |
+
deployments.append(
|
| 1327 |
+
ServerDeployment(
|
| 1328 |
+
shared_config=shared_config,
|
| 1329 |
+
mode_config=dflash_config,
|
| 1330 |
+
)
|
| 1331 |
+
)
|
| 1332 |
+
else:
|
| 1333 |
+
raise ValueError(f"Unknown speculative mode: {spec_mode}")
|
| 1334 |
+
return deployments
|
| 1335 |
+
|
| 1336 |
+
|
| 1337 |
+
def _build_benchmark_jobs(
|
| 1338 |
+
config: SweepConfig, shared_configs: list[SharedServerConfig]
|
| 1339 |
+
) -> list[BenchmarkJob]:
|
| 1340 |
+
jobs: list[BenchmarkJob] = []
|
| 1341 |
+
for shared_config in shared_configs:
|
| 1342 |
+
deployments = _build_deployments(shared_config, config.deployment_sweep)
|
| 1343 |
+
for deployment in deployments:
|
| 1344 |
+
for workload in config.workloads:
|
| 1345 |
+
for concurrency in config.concurrencies:
|
| 1346 |
+
concurrency_deployment = replace(
|
| 1347 |
+
deployment,
|
| 1348 |
+
shared_config=replace(
|
| 1349 |
+
deployment.shared_config,
|
| 1350 |
+
max_running_requests=int(concurrency),
|
| 1351 |
+
cuda_graph_max_bs=int(concurrency),
|
| 1352 |
+
),
|
| 1353 |
+
)
|
| 1354 |
+
for run_index in range(config.methodology.runs_per_config):
|
| 1355 |
+
jobs.append(
|
| 1356 |
+
BenchmarkJob(
|
| 1357 |
+
target_model=config.target_model,
|
| 1358 |
+
workload=workload,
|
| 1359 |
+
deployment=concurrency_deployment,
|
| 1360 |
+
concurrency=concurrency,
|
| 1361 |
+
run_index=run_index,
|
| 1362 |
+
sampling=config.sampling,
|
| 1363 |
+
methodology=config.methodology,
|
| 1364 |
+
)
|
| 1365 |
+
)
|
| 1366 |
+
return jobs
|
| 1367 |
+
|
| 1368 |
+
|
| 1369 |
+
def _mode_display_name(mode: str) -> str:
|
| 1370 |
+
if mode.startswith("mtp_s"):
|
| 1371 |
+
return f"MTP steps={mode.removeprefix('mtp_s')}"
|
| 1372 |
+
if mode.startswith("dflash_b"):
|
| 1373 |
+
return f"DFLASH block={mode.removeprefix('dflash_b')}"
|
| 1374 |
+
return {
|
| 1375 |
+
"baseline": "Baseline",
|
| 1376 |
+
"dflash": "DFLASH",
|
| 1377 |
+
}.get(mode, mode)
|
| 1378 |
+
|
| 1379 |
+
|
| 1380 |
+
def _collect_metric(
|
| 1381 |
+
*,
|
| 1382 |
+
results: dict[tuple[str, int, int, str], BenchMetrics],
|
| 1383 |
+
backend: str,
|
| 1384 |
+
tp_sizes: list[int],
|
| 1385 |
+
concurrencies: list[int],
|
| 1386 |
+
mode: str,
|
| 1387 |
+
field: str,
|
| 1388 |
+
) -> dict[tuple[int, int], Optional[float]]:
|
| 1389 |
+
out: dict[tuple[int, int], Optional[float]] = {}
|
| 1390 |
+
for tp in tp_sizes:
|
| 1391 |
+
for conc in concurrencies:
|
| 1392 |
+
metrics = results.get((backend, tp, conc, mode), None)
|
| 1393 |
+
out[(tp, conc)] = None if metrics is None else getattr(metrics, field)
|
| 1394 |
+
return out
|
| 1395 |
+
|
| 1396 |
+
|
| 1397 |
+
def _compute_speedup(
|
| 1398 |
+
baseline: dict[tuple[int, int], Optional[float]],
|
| 1399 |
+
speculative: dict[tuple[int, int], Optional[float]],
|
| 1400 |
+
) -> dict[tuple[int, int], Optional[float]]:
|
| 1401 |
+
return {
|
| 1402 |
+
key: None if (b is None or d is None or b <= 0) else (d / b)
|
| 1403 |
+
for key, b in baseline.items()
|
| 1404 |
+
for d in [speculative.get(key, None)]
|
| 1405 |
+
}
|
| 1406 |
+
|
| 1407 |
+
|
| 1408 |
+
def _metric_map_from_config_results(
|
| 1409 |
+
config_results: list[ConfigResult],
|
| 1410 |
+
) -> dict[tuple[str, int, int, str], BenchMetrics]:
|
| 1411 |
+
return {
|
| 1412 |
+
result.key.metric_key(): result.metrics
|
| 1413 |
+
for result in config_results
|
| 1414 |
+
if result.metrics is not None
|
| 1415 |
+
}
|
| 1416 |
+
|
| 1417 |
+
|
| 1418 |
+
def _print_kv_lines(items: list[tuple[str, object]]) -> None:
|
| 1419 |
+
for key, value in items:
|
| 1420 |
+
print(f"{key}={value}")
|
| 1421 |
+
|
| 1422 |
+
|
| 1423 |
+
def _print_failure_summary(config_results: list[ConfigResult]) -> None:
|
| 1424 |
+
failed_results = [
|
| 1425 |
+
result for result in config_results if result.failed_run_count > 0
|
| 1426 |
+
]
|
| 1427 |
+
if not failed_results:
|
| 1428 |
+
return
|
| 1429 |
+
|
| 1430 |
+
print("\n=== Failed/Partial Runs ===")
|
| 1431 |
+
for result in failed_results:
|
| 1432 |
+
key = result.key
|
| 1433 |
+
print(
|
| 1434 |
+
f"workload={key.workload} backend={key.backend} tp={key.tp} "
|
| 1435 |
+
f"mode={key.mode} conc={key.concurrency} status={result.status} "
|
| 1436 |
+
f"successful_runs={result.successful_run_count} "
|
| 1437 |
+
f"failed_runs={result.failed_run_count} "
|
| 1438 |
+
f"successful_run_numbers={_format_successful_run_numbers(result)} "
|
| 1439 |
+
f"failed_run_numbers={_format_failed_run_numbers(result.failures)} "
|
| 1440 |
+
f"errors={_format_failure_messages(result.failures)}"
|
| 1441 |
+
)
|
| 1442 |
+
|
| 1443 |
+
|
| 1444 |
+
def _server_env_for_job(job: BenchmarkJob) -> dict[str, str]:
|
| 1445 |
+
return {
|
| 1446 |
+
"SGLANG_ENABLE_OVERLAP_PLAN_STREAM": (
|
| 1447 |
+
"1" if job.deployment.enable_overlap_plan_stream else "0"
|
| 1448 |
+
),
|
| 1449 |
+
"SGLANG_PYSPY_DUMP_BEFORE_CRASH": "0",
|
| 1450 |
+
"SGLANG_CUDA_COREDUMP_BEFORE_CRASH": "0",
|
| 1451 |
+
}
|
| 1452 |
+
|
| 1453 |
+
|
| 1454 |
+
def _run_benchmark_job(job: BenchmarkJob) -> JobResult:
|
| 1455 |
+
from sglang.test.test_utils import (
|
| 1456 |
+
DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH as SGLANG_DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
|
| 1457 |
+
find_available_port,
|
| 1458 |
+
popen_launch_server,
|
| 1459 |
+
)
|
| 1460 |
+
from transformers import AutoTokenizer
|
| 1461 |
+
|
| 1462 |
+
key = job.key
|
| 1463 |
+
print(f"\n=== {job.label} {job.run_label} ===")
|
| 1464 |
+
samples = _load_user_turns(job.workload)
|
| 1465 |
+
if not samples:
|
| 1466 |
+
raise RuntimeError(f"Workload '{job.workload}' did not produce any prompts.")
|
| 1467 |
+
|
| 1468 |
+
source_sample_count = len(samples)
|
| 1469 |
+
source_generation_turn_count = _generation_turn_count(samples)
|
| 1470 |
+
plan = _build_benchmark_plan(
|
| 1471 |
+
samples,
|
| 1472 |
+
concurrency=job.concurrency,
|
| 1473 |
+
methodology=job.methodology,
|
| 1474 |
+
)
|
| 1475 |
+
if plan.measured_sample_count > source_sample_count:
|
| 1476 |
+
print(
|
| 1477 |
+
"[config] measured sample count exceeds workload size; "
|
| 1478 |
+
"repeating whole workload copies with radix cache enabled."
|
| 1479 |
+
)
|
| 1480 |
+
|
| 1481 |
+
base_url = f"http://127.0.0.1:{find_available_port(20000)}"
|
| 1482 |
+
tokenizer = AutoTokenizer.from_pretrained(job.target_model)
|
| 1483 |
+
server_start_timeout_s = int(
|
| 1484 |
+
max(SGLANG_DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH, job.methodology.timeout_s)
|
| 1485 |
+
)
|
| 1486 |
+
server_env = _server_env_for_job(job)
|
| 1487 |
+
print(
|
| 1488 |
+
"server_env="
|
| 1489 |
+
+ ",".join(f"{key}:{value}" for key, value in sorted(server_env.items()))
|
| 1490 |
+
)
|
| 1491 |
+
proc = popen_launch_server(
|
| 1492 |
+
job.target_model,
|
| 1493 |
+
base_url,
|
| 1494 |
+
timeout=server_start_timeout_s,
|
| 1495 |
+
other_args=job.deployment.server_args,
|
| 1496 |
+
env=server_env,
|
| 1497 |
+
)
|
| 1498 |
+
try:
|
| 1499 |
+
_send_generate(
|
| 1500 |
+
base_url,
|
| 1501 |
+
"Hello",
|
| 1502 |
+
max_new_tokens=8,
|
| 1503 |
+
temperature=job.sampling.temperature,
|
| 1504 |
+
top_p=job.sampling.top_p,
|
| 1505 |
+
top_k=job.sampling.top_k,
|
| 1506 |
+
timeout_s=min(job.methodology.timeout_s, 300),
|
| 1507 |
+
)
|
| 1508 |
+
|
| 1509 |
+
_flush_cache(base_url)
|
| 1510 |
+
print(
|
| 1511 |
+
f"[warmup {job.run_label}] run {len(plan.warmup_samples)} samples / "
|
| 1512 |
+
f"{plan.warmup_generation_turn_count} generation turns after "
|
| 1513 |
+
"/flush_cache; excluded from metrics."
|
| 1514 |
+
)
|
| 1515 |
+
_run_unmeasured_requests(
|
| 1516 |
+
base_url,
|
| 1517 |
+
samples=plan.warmup_samples,
|
| 1518 |
+
tokenizer=tokenizer,
|
| 1519 |
+
sampling=job.sampling,
|
| 1520 |
+
concurrency=job.concurrency,
|
| 1521 |
+
timeout_s=job.methodology.timeout_s,
|
| 1522 |
+
)
|
| 1523 |
+
_flush_cache(base_url)
|
| 1524 |
+
print(
|
| 1525 |
+
f"[warmup {job.run_label}] flushed cache after warmup; "
|
| 1526 |
+
"starting measured workload."
|
| 1527 |
+
)
|
| 1528 |
+
metrics = _run_requests(
|
| 1529 |
+
base_url,
|
| 1530 |
+
samples=plan.measured_samples,
|
| 1531 |
+
warmdown_samples=plan.warmdown_samples,
|
| 1532 |
+
tokenizer=tokenizer,
|
| 1533 |
+
sampling=job.sampling,
|
| 1534 |
+
concurrency=job.concurrency,
|
| 1535 |
+
timeout_s=job.methodology.timeout_s,
|
| 1536 |
+
expect_spec=job.deployment.expect_spec,
|
| 1537 |
+
)
|
| 1538 |
+
line = (
|
| 1539 |
+
f"[{job.label} {job.run_label}] samples={plan.measured_sample_count:<4} "
|
| 1540 |
+
f"turns={plan.measured_generation_turn_count:<4} "
|
| 1541 |
+
f"toks/s={metrics.output_toks_per_s:,.2f} "
|
| 1542 |
+
f"latency={metrics.latency_s:.1f}s "
|
| 1543 |
+
f"warmup_turns={plan.warmup_generation_turn_count} "
|
| 1544 |
+
f"warmdown_turns={plan.warmdown_generation_turn_count}"
|
| 1545 |
+
)
|
| 1546 |
+
if job.deployment.expect_spec:
|
| 1547 |
+
accept_len = (
|
| 1548 |
+
"N/A"
|
| 1549 |
+
if metrics.spec_accept_length is None
|
| 1550 |
+
else f"{metrics.spec_accept_length:.3f}"
|
| 1551 |
+
)
|
| 1552 |
+
line += (
|
| 1553 |
+
f" accept_len_mean={accept_len} "
|
| 1554 |
+
f"spec_verify_ct_sum={metrics.spec_verify_ct_sum}"
|
| 1555 |
+
)
|
| 1556 |
+
print(line)
|
| 1557 |
+
return JobResult(
|
| 1558 |
+
key=key,
|
| 1559 |
+
deployment=job.deployment,
|
| 1560 |
+
source_sample_count=source_sample_count,
|
| 1561 |
+
source_generation_turn_count=source_generation_turn_count,
|
| 1562 |
+
warmup_generation_turn_count=plan.warmup_generation_turn_count,
|
| 1563 |
+
warmdown_generation_turn_count=plan.warmdown_generation_turn_count,
|
| 1564 |
+
run_index=job.run_index,
|
| 1565 |
+
metrics=metrics,
|
| 1566 |
+
)
|
| 1567 |
+
finally:
|
| 1568 |
+
_shutdown_server(
|
| 1569 |
+
proc,
|
| 1570 |
+
base_url,
|
| 1571 |
+
drain_timeout_s=job.methodology.server_shutdown_drain_timeout_s,
|
| 1572 |
+
kill_timeout_s=job.methodology.server_shutdown_timeout_s,
|
| 1573 |
+
)
|
| 1574 |
+
|
| 1575 |
+
|
| 1576 |
+
def _run_benchmark_job_gracefully(job: BenchmarkJob) -> JobResult | JobFailure:
|
| 1577 |
+
try:
|
| 1578 |
+
return _run_benchmark_job(job)
|
| 1579 |
+
except Exception as exc:
|
| 1580 |
+
error_type = type(exc).__name__
|
| 1581 |
+
error_message = _one_line(str(exc) or repr(exc))
|
| 1582 |
+
print(
|
| 1583 |
+
f"[failed {job.label} {job.run_label}] "
|
| 1584 |
+
f"{error_type}: {error_message}"
|
| 1585 |
+
)
|
| 1586 |
+
return JobFailure(
|
| 1587 |
+
key=job.key,
|
| 1588 |
+
deployment=job.deployment,
|
| 1589 |
+
run_index=job.run_index,
|
| 1590 |
+
error_type=error_type,
|
| 1591 |
+
error_message=error_message,
|
| 1592 |
+
)
|
| 1593 |
+
|
| 1594 |
+
|
| 1595 |
+
def _print_summary(
|
| 1596 |
+
*,
|
| 1597 |
+
config: SweepConfig,
|
| 1598 |
+
workload: str,
|
| 1599 |
+
config_results: list[ConfigResult],
|
| 1600 |
+
shared_configs: list[SharedServerConfig],
|
| 1601 |
+
attention_backends: list[str],
|
| 1602 |
+
tp_sizes: list[int],
|
| 1603 |
+
concurrencies: list[int],
|
| 1604 |
+
device_sm: int,
|
| 1605 |
+
mode_keys: list[str],
|
| 1606 |
+
source_sample_count: Optional[int],
|
| 1607 |
+
source_generation_turn_count: Optional[int],
|
| 1608 |
+
results: dict[tuple[str, int, int, str], BenchMetrics],
|
| 1609 |
+
) -> None:
|
| 1610 |
+
print("\n=== Speculative Benchmark Sweep Summary ===")
|
| 1611 |
+
_print_kv_lines(
|
| 1612 |
+
[
|
| 1613 |
+
("workload", workload),
|
| 1614 |
+
("source_sample_count", source_sample_count),
|
| 1615 |
+
("source_generation_turn_count", source_generation_turn_count),
|
| 1616 |
+
("target_model", config.target_model),
|
| 1617 |
+
("dflash_draft_model", config.dflash_draft_model),
|
| 1618 |
+
("spec_modes", ",".join(mode_keys)),
|
| 1619 |
+
(
|
| 1620 |
+
"mtp_num_steps",
|
| 1621 |
+
",".join(str(x) for x in config.deployment_sweep.mtp_num_steps),
|
| 1622 |
+
),
|
| 1623 |
+
(
|
| 1624 |
+
"mtp_num_draft_tokens",
|
| 1625 |
+
",".join(
|
| 1626 |
+
str(int(x) + 1)
|
| 1627 |
+
for x in config.deployment_sweep.mtp_num_steps
|
| 1628 |
+
),
|
| 1629 |
+
),
|
| 1630 |
+
("mtp_eagle_topk", 1),
|
| 1631 |
+
("max_new_tokens", config.sampling.max_new_tokens),
|
| 1632 |
+
("enable_thinking", bool(config.sampling.enable_thinking)),
|
| 1633 |
+
("timeout_s", config.methodology.timeout_s),
|
| 1634 |
+
(
|
| 1635 |
+
"server_shutdown_drain_timeout_s",
|
| 1636 |
+
config.methodology.server_shutdown_drain_timeout_s,
|
| 1637 |
+
),
|
| 1638 |
+
(
|
| 1639 |
+
"server_shutdown_timeout_s",
|
| 1640 |
+
config.methodology.server_shutdown_timeout_s,
|
| 1641 |
+
),
|
| 1642 |
+
(
|
| 1643 |
+
"shared_server_configs",
|
| 1644 |
+
";".join(
|
| 1645 |
+
server_config.summary_label()
|
| 1646 |
+
for server_config in shared_configs
|
| 1647 |
+
),
|
| 1648 |
+
),
|
| 1649 |
+
(
|
| 1650 |
+
"sampling",
|
| 1651 |
+
f"temperature:{config.sampling.temperature}, "
|
| 1652 |
+
f"top_p:{config.sampling.top_p}, top_k:{config.sampling.top_k}",
|
| 1653 |
+
),
|
| 1654 |
+
("attention_backends", ",".join(attention_backends)),
|
| 1655 |
+
(
|
| 1656 |
+
"dflash_block_sizes",
|
| 1657 |
+
",".join(
|
| 1658 |
+
"default" if x is None else str(x)
|
| 1659 |
+
for x in config.deployment_sweep.dflash_block_sizes
|
| 1660 |
+
),
|
| 1661 |
+
),
|
| 1662 |
+
("tp_sizes", ",".join(str(x) for x in tp_sizes)),
|
| 1663 |
+
("concurrencies", ",".join(str(x) for x in concurrencies)),
|
| 1664 |
+
("num_samples", config.methodology.num_samples),
|
| 1665 |
+
("runs_per_config", config.methodology.runs_per_config),
|
| 1666 |
+
(
|
| 1667 |
+
"min_generation_turns_per_config",
|
| 1668 |
+
config.methodology.min_generation_turns_per_config,
|
| 1669 |
+
),
|
| 1670 |
+
(
|
| 1671 |
+
"min_warmup_generation_turns",
|
| 1672 |
+
config.methodology.min_warmup_generation_turns,
|
| 1673 |
+
),
|
| 1674 |
+
("disable_radix_cache", False),
|
| 1675 |
+
("device_sm", device_sm),
|
| 1676 |
+
("skip_baseline", not config.deployment_sweep.include_baseline),
|
| 1677 |
+
]
|
| 1678 |
+
)
|
| 1679 |
+
_print_failure_summary(config_results)
|
| 1680 |
+
|
| 1681 |
+
for backend in attention_backends:
|
| 1682 |
+
print(f"\n=== Backend: {backend} ===")
|
| 1683 |
+
baseline_output_tps = _collect_metric(
|
| 1684 |
+
results=results,
|
| 1685 |
+
backend=backend,
|
| 1686 |
+
tp_sizes=tp_sizes,
|
| 1687 |
+
concurrencies=concurrencies,
|
| 1688 |
+
mode="baseline",
|
| 1689 |
+
field="output_toks_per_s",
|
| 1690 |
+
)
|
| 1691 |
+
sections: list[tuple[str, dict[tuple[int, int], Optional[float]], str]] = [
|
| 1692 |
+
("Baseline output tok/s", baseline_output_tps, ",.2f")
|
| 1693 |
+
]
|
| 1694 |
+
|
| 1695 |
+
for spec_mode in mode_keys:
|
| 1696 |
+
display_name = _mode_display_name(spec_mode)
|
| 1697 |
+
spec_output_tps = _collect_metric(
|
| 1698 |
+
results=results,
|
| 1699 |
+
backend=backend,
|
| 1700 |
+
tp_sizes=tp_sizes,
|
| 1701 |
+
concurrencies=concurrencies,
|
| 1702 |
+
mode=spec_mode,
|
| 1703 |
+
field="output_toks_per_s",
|
| 1704 |
+
)
|
| 1705 |
+
spec_accept_length = _collect_metric(
|
| 1706 |
+
results=results,
|
| 1707 |
+
backend=backend,
|
| 1708 |
+
tp_sizes=tp_sizes,
|
| 1709 |
+
concurrencies=concurrencies,
|
| 1710 |
+
mode=spec_mode,
|
| 1711 |
+
field="spec_accept_length",
|
| 1712 |
+
)
|
| 1713 |
+
sections.extend(
|
| 1714 |
+
[
|
| 1715 |
+
(f"{display_name} output tok/s", spec_output_tps, ",.2f"),
|
| 1716 |
+
(
|
| 1717 |
+
f"Speedup ({display_name} / baseline)",
|
| 1718 |
+
_compute_speedup(baseline_output_tps, spec_output_tps),
|
| 1719 |
+
".3f",
|
| 1720 |
+
),
|
| 1721 |
+
(
|
| 1722 |
+
f"{display_name} acceptance length (mean per generation turn)",
|
| 1723 |
+
spec_accept_length,
|
| 1724 |
+
".3f",
|
| 1725 |
+
),
|
| 1726 |
+
]
|
| 1727 |
+
)
|
| 1728 |
+
|
| 1729 |
+
for title, values, fmt in sections:
|
| 1730 |
+
print(f"\n{title}")
|
| 1731 |
+
print(
|
| 1732 |
+
_format_table(
|
| 1733 |
+
tp_sizes=tp_sizes,
|
| 1734 |
+
concurrencies=concurrencies,
|
| 1735 |
+
values=values,
|
| 1736 |
+
float_fmt=fmt,
|
| 1737 |
+
)
|
| 1738 |
+
)
|
| 1739 |
+
|
| 1740 |
+
|
| 1741 |
+
CSV_FIELDS = [
|
| 1742 |
+
"workload",
|
| 1743 |
+
"backend",
|
| 1744 |
+
"tp",
|
| 1745 |
+
"mode",
|
| 1746 |
+
"mtp_num_steps",
|
| 1747 |
+
"dflash_block_size",
|
| 1748 |
+
"concurrency",
|
| 1749 |
+
"source_sample_count",
|
| 1750 |
+
"source_generation_turn_count",
|
| 1751 |
+
"runs_per_config",
|
| 1752 |
+
"successful_runs",
|
| 1753 |
+
"failed_runs",
|
| 1754 |
+
"status",
|
| 1755 |
+
"successful_run_numbers",
|
| 1756 |
+
"failed_run_numbers",
|
| 1757 |
+
"failure_messages",
|
| 1758 |
+
"measured_sample_count",
|
| 1759 |
+
"measured_generation_turn_count",
|
| 1760 |
+
"output_toks_per_s",
|
| 1761 |
+
"output_toks_per_s_std",
|
| 1762 |
+
"latency_s",
|
| 1763 |
+
"latency_s_std",
|
| 1764 |
+
"output_tokens",
|
| 1765 |
+
"speedup_vs_baseline",
|
| 1766 |
+
"accept_length_mean_from_conc1",
|
| 1767 |
+
"accept_length_mean_this_conc",
|
| 1768 |
+
"accept_length_mean_this_conc_std",
|
| 1769 |
+
"spec_verify_ct_sum",
|
| 1770 |
+
]
|
| 1771 |
+
|
| 1772 |
+
|
| 1773 |
+
def _one_line(value: str) -> str:
|
| 1774 |
+
return " ".join(str(value).split())
|
| 1775 |
+
|
| 1776 |
+
|
| 1777 |
+
def _fmt_optional_int(value: Optional[int]) -> str:
|
| 1778 |
+
if value is None:
|
| 1779 |
+
return ""
|
| 1780 |
+
return str(value)
|
| 1781 |
+
|
| 1782 |
+
|
| 1783 |
+
def _fmt_csv_value(value: Optional[float]) -> str:
|
| 1784 |
+
if value is None:
|
| 1785 |
+
return ""
|
| 1786 |
+
return f"{value:.6f}"
|
| 1787 |
+
|
| 1788 |
+
|
| 1789 |
+
def _format_failed_run_numbers(failures: tuple[JobFailure, ...]) -> str:
|
| 1790 |
+
return ",".join(str(failure.run_index + 1) for failure in failures)
|
| 1791 |
+
|
| 1792 |
+
|
| 1793 |
+
def _format_successful_run_numbers(result: ConfigResult) -> str:
|
| 1794 |
+
return ",".join(
|
| 1795 |
+
str(run_index + 1) for run_index in result.successful_run_indices
|
| 1796 |
+
)
|
| 1797 |
+
|
| 1798 |
+
|
| 1799 |
+
def _format_failure_messages(failures: tuple[JobFailure, ...]) -> str:
|
| 1800 |
+
return " | ".join(
|
| 1801 |
+
(
|
| 1802 |
+
f"run={failure.run_index + 1} "
|
| 1803 |
+
f"{failure.error_type}: {failure.error_message}"
|
| 1804 |
+
)
|
| 1805 |
+
for failure in failures
|
| 1806 |
+
)
|
| 1807 |
+
|
| 1808 |
+
|
| 1809 |
+
def _mean_optional(values: list[Optional[float]]) -> Optional[float]:
|
| 1810 |
+
present_values = [value for value in values if value is not None]
|
| 1811 |
+
if not present_values:
|
| 1812 |
+
return None
|
| 1813 |
+
return float(statistics.mean(present_values))
|
| 1814 |
+
|
| 1815 |
+
|
| 1816 |
+
def _stdev_optional(values: list[Optional[float]]) -> Optional[float]:
|
| 1817 |
+
present_values = [value for value in values if value is not None]
|
| 1818 |
+
if len(present_values) < 2:
|
| 1819 |
+
return None
|
| 1820 |
+
return float(statistics.stdev(present_values))
|
| 1821 |
+
|
| 1822 |
+
|
| 1823 |
+
def _metric_stdev(
|
| 1824 |
+
metrics: tuple[BenchMetrics, ...], field: str
|
| 1825 |
+
) -> Optional[float]:
|
| 1826 |
+
return _stdev_optional([getattr(metric, field) for metric in metrics])
|
| 1827 |
+
|
| 1828 |
+
|
| 1829 |
+
def _aggregate_bench_metrics(metrics: list[BenchMetrics]) -> BenchMetrics:
|
| 1830 |
+
if not metrics:
|
| 1831 |
+
raise RuntimeError("Cannot aggregate an empty metrics list.")
|
| 1832 |
+
first = metrics[0]
|
| 1833 |
+
return BenchMetrics(
|
| 1834 |
+
sample_count=first.sample_count,
|
| 1835 |
+
generation_turn_count=first.generation_turn_count,
|
| 1836 |
+
latency_s=float(statistics.mean(metric.latency_s for metric in metrics)),
|
| 1837 |
+
output_tokens=int(
|
| 1838 |
+
round(statistics.mean(metric.output_tokens for metric in metrics))
|
| 1839 |
+
),
|
| 1840 |
+
output_toks_per_s=float(
|
| 1841 |
+
statistics.mean(metric.output_toks_per_s for metric in metrics)
|
| 1842 |
+
),
|
| 1843 |
+
spec_accept_length=_mean_optional(
|
| 1844 |
+
[metric.spec_accept_length for metric in metrics]
|
| 1845 |
+
),
|
| 1846 |
+
spec_verify_ct_sum=int(
|
| 1847 |
+
round(statistics.mean(metric.spec_verify_ct_sum for metric in metrics))
|
| 1848 |
+
),
|
| 1849 |
+
)
|
| 1850 |
+
|
| 1851 |
+
|
| 1852 |
+
def _aggregate_job_results(
|
| 1853 |
+
job_results: list[JobResult | JobFailure],
|
| 1854 |
+
) -> list[ConfigResult]:
|
| 1855 |
+
grouped_results: dict[RunKey, list[JobResult | JobFailure]] = {}
|
| 1856 |
+
ordered_keys: list[RunKey] = []
|
| 1857 |
+
for result in job_results:
|
| 1858 |
+
if result.key not in grouped_results:
|
| 1859 |
+
grouped_results[result.key] = []
|
| 1860 |
+
ordered_keys.append(result.key)
|
| 1861 |
+
grouped_results[result.key].append(result)
|
| 1862 |
+
|
| 1863 |
+
config_results: list[ConfigResult] = []
|
| 1864 |
+
for key in ordered_keys:
|
| 1865 |
+
results = sorted(grouped_results[key], key=lambda result: result.run_index)
|
| 1866 |
+
successful_results = [
|
| 1867 |
+
result for result in results if isinstance(result, JobResult)
|
| 1868 |
+
]
|
| 1869 |
+
failures = tuple(
|
| 1870 |
+
result for result in results if isinstance(result, JobFailure)
|
| 1871 |
+
)
|
| 1872 |
+
first = results[0]
|
| 1873 |
+
first_success = successful_results[0] if successful_results else None
|
| 1874 |
+
repeat_metrics = tuple(result.metrics for result in successful_results)
|
| 1875 |
+
successful_run_indices = tuple(
|
| 1876 |
+
result.run_index for result in successful_results
|
| 1877 |
+
)
|
| 1878 |
+
metrics = (
|
| 1879 |
+
_aggregate_bench_metrics(list(repeat_metrics))
|
| 1880 |
+
if repeat_metrics
|
| 1881 |
+
else None
|
| 1882 |
+
)
|
| 1883 |
+
config_results.append(
|
| 1884 |
+
ConfigResult(
|
| 1885 |
+
key=key,
|
| 1886 |
+
deployment=first.deployment,
|
| 1887 |
+
source_sample_count=(
|
| 1888 |
+
None if first_success is None else first_success.source_sample_count
|
| 1889 |
+
),
|
| 1890 |
+
source_generation_turn_count=(
|
| 1891 |
+
None
|
| 1892 |
+
if first_success is None
|
| 1893 |
+
else first_success.source_generation_turn_count
|
| 1894 |
+
),
|
| 1895 |
+
warmup_generation_turn_count=(
|
| 1896 |
+
None
|
| 1897 |
+
if first_success is None
|
| 1898 |
+
else first_success.warmup_generation_turn_count
|
| 1899 |
+
),
|
| 1900 |
+
warmdown_generation_turn_count=(
|
| 1901 |
+
None
|
| 1902 |
+
if first_success is None
|
| 1903 |
+
else first_success.warmdown_generation_turn_count
|
| 1904 |
+
),
|
| 1905 |
+
metrics=metrics,
|
| 1906 |
+
repeat_metrics=repeat_metrics,
|
| 1907 |
+
successful_run_indices=successful_run_indices,
|
| 1908 |
+
failures=failures,
|
| 1909 |
+
)
|
| 1910 |
+
)
|
| 1911 |
+
return config_results
|
| 1912 |
+
|
| 1913 |
+
|
| 1914 |
+
def _build_csv_rows(
|
| 1915 |
+
*,
|
| 1916 |
+
config_results: list[ConfigResult],
|
| 1917 |
+
) -> list[dict[str, object]]:
|
| 1918 |
+
rows: list[dict[str, object]] = []
|
| 1919 |
+
results_by_key = {result.key: result for result in config_results}
|
| 1920 |
+
|
| 1921 |
+
for result in config_results:
|
| 1922 |
+
key = result.key
|
| 1923 |
+
metrics = result.metrics
|
| 1924 |
+
baseline_result = results_by_key.get(
|
| 1925 |
+
RunKey(
|
| 1926 |
+
workload=key.workload,
|
| 1927 |
+
backend=key.backend,
|
| 1928 |
+
tp=key.tp,
|
| 1929 |
+
concurrency=key.concurrency,
|
| 1930 |
+
mode="baseline",
|
| 1931 |
+
)
|
| 1932 |
+
)
|
| 1933 |
+
speedup = None
|
| 1934 |
+
if (
|
| 1935 |
+
metrics is not None
|
| 1936 |
+
and key.mode != "baseline"
|
| 1937 |
+
and baseline_result is not None
|
| 1938 |
+
and baseline_result.metrics is not None
|
| 1939 |
+
and baseline_result.metrics.output_toks_per_s > 0
|
| 1940 |
+
):
|
| 1941 |
+
speedup = (
|
| 1942 |
+
metrics.output_toks_per_s
|
| 1943 |
+
/ baseline_result.metrics.output_toks_per_s
|
| 1944 |
+
)
|
| 1945 |
+
|
| 1946 |
+
accept_source_result = results_by_key.get(
|
| 1947 |
+
RunKey(
|
| 1948 |
+
workload=key.workload,
|
| 1949 |
+
backend=key.backend,
|
| 1950 |
+
tp=key.tp,
|
| 1951 |
+
concurrency=1,
|
| 1952 |
+
mode=key.mode,
|
| 1953 |
+
)
|
| 1954 |
+
)
|
| 1955 |
+
accept_length_from_conc1 = (
|
| 1956 |
+
None
|
| 1957 |
+
if accept_source_result is None or accept_source_result.metrics is None
|
| 1958 |
+
else accept_source_result.metrics.spec_accept_length
|
| 1959 |
+
)
|
| 1960 |
+
|
| 1961 |
+
rows.append(
|
| 1962 |
+
{
|
| 1963 |
+
"workload": key.workload,
|
| 1964 |
+
"backend": key.backend,
|
| 1965 |
+
"tp": key.tp,
|
| 1966 |
+
"mode": key.mode,
|
| 1967 |
+
"mtp_num_steps": result.deployment.mtp_num_steps or "",
|
| 1968 |
+
"dflash_block_size": result.deployment.dflash_block_size or "",
|
| 1969 |
+
"concurrency": key.concurrency,
|
| 1970 |
+
"source_sample_count": _fmt_optional_int(result.source_sample_count),
|
| 1971 |
+
"source_generation_turn_count": _fmt_optional_int(
|
| 1972 |
+
result.source_generation_turn_count
|
| 1973 |
+
),
|
| 1974 |
+
"runs_per_config": result.run_count,
|
| 1975 |
+
"successful_runs": result.successful_run_count,
|
| 1976 |
+
"failed_runs": result.failed_run_count,
|
| 1977 |
+
"status": result.status,
|
| 1978 |
+
"successful_run_numbers": _format_successful_run_numbers(result),
|
| 1979 |
+
"failed_run_numbers": _format_failed_run_numbers(result.failures),
|
| 1980 |
+
"failure_messages": _format_failure_messages(result.failures),
|
| 1981 |
+
"measured_sample_count": (
|
| 1982 |
+
"" if metrics is None else metrics.sample_count
|
| 1983 |
+
),
|
| 1984 |
+
"measured_generation_turn_count": (
|
| 1985 |
+
"" if metrics is None else metrics.generation_turn_count
|
| 1986 |
+
),
|
| 1987 |
+
"output_toks_per_s": _fmt_csv_value(
|
| 1988 |
+
None if metrics is None else metrics.output_toks_per_s
|
| 1989 |
+
),
|
| 1990 |
+
"output_toks_per_s_std": _fmt_csv_value(
|
| 1991 |
+
_metric_stdev(result.repeat_metrics, "output_toks_per_s")
|
| 1992 |
+
),
|
| 1993 |
+
"latency_s": _fmt_csv_value(
|
| 1994 |
+
None if metrics is None else metrics.latency_s
|
| 1995 |
+
),
|
| 1996 |
+
"latency_s_std": _fmt_csv_value(
|
| 1997 |
+
_metric_stdev(result.repeat_metrics, "latency_s")
|
| 1998 |
+
),
|
| 1999 |
+
"output_tokens": "" if metrics is None else metrics.output_tokens,
|
| 2000 |
+
"speedup_vs_baseline": _fmt_csv_value(speedup),
|
| 2001 |
+
"accept_length_mean_from_conc1": _fmt_csv_value(
|
| 2002 |
+
accept_length_from_conc1
|
| 2003 |
+
),
|
| 2004 |
+
"accept_length_mean_this_conc": _fmt_csv_value(
|
| 2005 |
+
None if metrics is None else metrics.spec_accept_length
|
| 2006 |
+
),
|
| 2007 |
+
"accept_length_mean_this_conc_std": _fmt_csv_value(
|
| 2008 |
+
_metric_stdev(result.repeat_metrics, "spec_accept_length")
|
| 2009 |
+
),
|
| 2010 |
+
"spec_verify_ct_sum": (
|
| 2011 |
+
"" if metrics is None else metrics.spec_verify_ct_sum
|
| 2012 |
+
),
|
| 2013 |
+
}
|
| 2014 |
+
)
|
| 2015 |
+
return rows
|
| 2016 |
+
|
| 2017 |
+
|
| 2018 |
+
def _print_csv_summary(rows: list[dict[str, object]]) -> None:
|
| 2019 |
+
buffer = io.StringIO()
|
| 2020 |
+
writer = csv.DictWriter(buffer, fieldnames=CSV_FIELDS)
|
| 2021 |
+
writer.writeheader()
|
| 2022 |
+
writer.writerows(rows)
|
| 2023 |
+
print("\n=== CSV Summary ===")
|
| 2024 |
+
print(buffer.getvalue(), end="", flush=True)
|
| 2025 |
+
|
| 2026 |
+
|
| 2027 |
+
def _write_csv_summary(path: str, rows: list[dict[str, object]]) -> None:
|
| 2028 |
+
out_path = Path(path)
|
| 2029 |
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
| 2030 |
+
with open(out_path, "w", newline="") as f:
|
| 2031 |
+
writer = csv.DictWriter(f, fieldnames=CSV_FIELDS)
|
| 2032 |
+
writer.writeheader()
|
| 2033 |
+
writer.writerows(rows)
|
| 2034 |
+
print(f"[csv] wrote {len(rows)} rows to {out_path}", flush=True)
|
| 2035 |
+
|
| 2036 |
+
|
| 2037 |
+
def parse_args(argv: Optional[list[str]] = None) -> argparse.Namespace:
|
| 2038 |
+
parser = argparse.ArgumentParser()
|
| 2039 |
+
parser.add_argument(
|
| 2040 |
+
"--workloads",
|
| 2041 |
+
dest="workloads",
|
| 2042 |
+
default=DEFAULT_WORKLOADS,
|
| 2043 |
+
help=(
|
| 2044 |
+
"Comma-separated workloads to run, or `all`."
|
| 2045 |
+
),
|
| 2046 |
+
)
|
| 2047 |
+
parser.add_argument(
|
| 2048 |
+
"--csv-output",
|
| 2049 |
+
default=None,
|
| 2050 |
+
help="Optional path to write the final CSV summary.",
|
| 2051 |
+
)
|
| 2052 |
+
parser.add_argument("--target-model", default="Qwen/Qwen3.5-397B-A17B")
|
| 2053 |
+
parser.add_argument(
|
| 2054 |
+
"--dflash-draft-model",
|
| 2055 |
+
dest="dflash_draft_model",
|
| 2056 |
+
default=None,
|
| 2057 |
+
help="Required when --spec-modes includes dflash.",
|
| 2058 |
+
)
|
| 2059 |
+
parser.add_argument(
|
| 2060 |
+
"--spec-modes",
|
| 2061 |
+
default="mtp",
|
| 2062 |
+
help="Comma-separated speculative modes to benchmark. Supported: mtp,dflash.",
|
| 2063 |
+
)
|
| 2064 |
+
parser.add_argument(
|
| 2065 |
+
"--mtp-num-steps",
|
| 2066 |
+
default="3",
|
| 2067 |
+
help=(
|
| 2068 |
+
"Comma-separated MTP/EAGLE speculative num steps. num draft tokens "
|
| 2069 |
+
"is always num_steps + 1."
|
| 2070 |
+
),
|
| 2071 |
+
)
|
| 2072 |
+
parser.add_argument(
|
| 2073 |
+
"--skip-baseline",
|
| 2074 |
+
action="store_true",
|
| 2075 |
+
help="Skip running the baseline (target-only) sweep; only run speculative modes and report N/A for baseline/speedup.",
|
| 2076 |
+
)
|
| 2077 |
+
thinking_group = parser.add_mutually_exclusive_group()
|
| 2078 |
+
thinking_group.add_argument(
|
| 2079 |
+
"--enable-thinking",
|
| 2080 |
+
dest="enable_thinking",
|
| 2081 |
+
action="store_true",
|
| 2082 |
+
default=True,
|
| 2083 |
+
help="Pass enable_thinking=True when applying the model chat template (default).",
|
| 2084 |
+
)
|
| 2085 |
+
thinking_group.add_argument(
|
| 2086 |
+
"--disable-thinking",
|
| 2087 |
+
dest="enable_thinking",
|
| 2088 |
+
action="store_false",
|
| 2089 |
+
help="Pass enable_thinking=False when applying the model chat template.",
|
| 2090 |
+
)
|
| 2091 |
+
parser.add_argument("--max-new-tokens", type=int, default=4096)
|
| 2092 |
+
parser.add_argument("--temperature", type=float, default=0.0)
|
| 2093 |
+
parser.add_argument("--top-p", type=float, default=1.0)
|
| 2094 |
+
parser.add_argument("--top-k", type=int, default=1)
|
| 2095 |
+
parser.add_argument("--concurrencies", default="1,32")
|
| 2096 |
+
parser.add_argument(
|
| 2097 |
+
"--num-samples",
|
| 2098 |
+
dest="num_samples",
|
| 2099 |
+
type=int,
|
| 2100 |
+
default=None,
|
| 2101 |
+
help=(
|
| 2102 |
+
"Exact number of measured samples per config. Repeats the selected "
|
| 2103 |
+
"workload if this exceeds the workload size. Default: unset."
|
| 2104 |
+
),
|
| 2105 |
+
)
|
| 2106 |
+
parser.add_argument(
|
| 2107 |
+
"--runs-per-config",
|
| 2108 |
+
type=int,
|
| 2109 |
+
default=1,
|
| 2110 |
+
help=(
|
| 2111 |
+
"Number of repeated measured runs per benchmark configuration. "
|
| 2112 |
+
"The final reported metrics are averaged across these runs."
|
| 2113 |
+
),
|
| 2114 |
+
)
|
| 2115 |
+
parser.add_argument(
|
| 2116 |
+
"--min-generation-turns-per-config",
|
| 2117 |
+
dest="min_generation_turns_per_config",
|
| 2118 |
+
type=int,
|
| 2119 |
+
default=1024,
|
| 2120 |
+
help=(
|
| 2121 |
+
"When --num-samples is unset and concurrency > 1, repeat whole workload "
|
| 2122 |
+
"copies until each config measures at least this many generation turns. "
|
| 2123 |
+
"Use 0 for one full workload copy."
|
| 2124 |
+
),
|
| 2125 |
+
)
|
| 2126 |
+
parser.add_argument(
|
| 2127 |
+
"--min-warmup-generation-turns",
|
| 2128 |
+
type=int,
|
| 2129 |
+
default=8,
|
| 2130 |
+
help=(
|
| 2131 |
+
"Minimum generation turns to run after /flush_cache before measured "
|
| 2132 |
+
"timing. Effective warmup is max(this value, 2 * concurrency)."
|
| 2133 |
+
),
|
| 2134 |
+
)
|
| 2135 |
+
parser.add_argument(
|
| 2136 |
+
"--dflash-block-sizes",
|
| 2137 |
+
default="default",
|
| 2138 |
+
help=(
|
| 2139 |
+
"Comma-separated DFlash block-size sweep. Use `default` to omit "
|
| 2140 |
+
"--speculative-dflash-block-size and let the server choose."
|
| 2141 |
+
),
|
| 2142 |
+
)
|
| 2143 |
+
args = parser.parse_args(argv)
|
| 2144 |
+
try:
|
| 2145 |
+
workloads = _parse_workload_selection(args.workloads)
|
| 2146 |
+
except ValueError as exc:
|
| 2147 |
+
parser.error(str(exc))
|
| 2148 |
+
|
| 2149 |
+
spec_modes = _parse_str_csv(args.spec_modes)
|
| 2150 |
+
supported_spec_modes = {"mtp", "dflash"}
|
| 2151 |
+
unknown_spec_modes = sorted(set(spec_modes) - supported_spec_modes)
|
| 2152 |
+
if unknown_spec_modes:
|
| 2153 |
+
parser.error(
|
| 2154 |
+
"--spec-modes contains unsupported values: "
|
| 2155 |
+
+ ",".join(unknown_spec_modes)
|
| 2156 |
+
)
|
| 2157 |
+
if not spec_modes:
|
| 2158 |
+
parser.error("--spec-modes must include at least one mode: mtp or dflash")
|
| 2159 |
+
if "dflash" in spec_modes and not args.dflash_draft_model:
|
| 2160 |
+
parser.error(
|
| 2161 |
+
"--dflash-draft-model is required when --spec-modes includes dflash"
|
| 2162 |
+
)
|
| 2163 |
+
try:
|
| 2164 |
+
dflash_block_sizes = _parse_optional_int_csv(str(args.dflash_block_sizes))
|
| 2165 |
+
except ValueError as exc:
|
| 2166 |
+
parser.error(f"--dflash-block-sizes must be integers/default: {exc}")
|
| 2167 |
+
if any(x is not None and x <= 0 for x in dflash_block_sizes):
|
| 2168 |
+
parser.error(
|
| 2169 |
+
"--dflash-block-sizes values must be > 0, "
|
| 2170 |
+
f"got {args.dflash_block_sizes}"
|
| 2171 |
+
)
|
| 2172 |
+
mtp_num_steps = _parse_int_csv(str(args.mtp_num_steps))
|
| 2173 |
+
if not mtp_num_steps:
|
| 2174 |
+
parser.error("--mtp-num-steps must include at least one positive integer")
|
| 2175 |
+
if any(x <= 0 for x in mtp_num_steps):
|
| 2176 |
+
parser.error(f"--mtp-num-steps values must be > 0, got {args.mtp_num_steps}")
|
| 2177 |
+
mode_keys = DeploymentSweep(
|
| 2178 |
+
include_baseline=not args.skip_baseline,
|
| 2179 |
+
spec_modes=tuple(spec_modes),
|
| 2180 |
+
mtp_num_steps=tuple(mtp_num_steps),
|
| 2181 |
+
dflash_draft_model=args.dflash_draft_model,
|
| 2182 |
+
dflash_block_sizes=tuple(dflash_block_sizes),
|
| 2183 |
+
).mode_keys
|
| 2184 |
+
duplicate_mode_keys = _duplicate_values(mode_keys)
|
| 2185 |
+
if duplicate_mode_keys:
|
| 2186 |
+
parser.error(
|
| 2187 |
+
"Duplicate deployment modes from sweep flags: "
|
| 2188 |
+
+ ",".join(duplicate_mode_keys)
|
| 2189 |
+
)
|
| 2190 |
+
args.workloads = workloads
|
| 2191 |
+
args.spec_modes = spec_modes
|
| 2192 |
+
args.mtp_num_steps = mtp_num_steps
|
| 2193 |
+
args.dflash_block_sizes = dflash_block_sizes
|
| 2194 |
+
return args
|
| 2195 |
+
|
| 2196 |
+
|
| 2197 |
+
def build_sweep_config_from_args(args: argparse.Namespace) -> SweepConfig:
|
| 2198 |
+
sampling = SamplingConfig(
|
| 2199 |
+
enable_thinking=bool(args.enable_thinking),
|
| 2200 |
+
max_new_tokens=int(args.max_new_tokens),
|
| 2201 |
+
temperature=float(args.temperature),
|
| 2202 |
+
top_p=float(args.top_p),
|
| 2203 |
+
top_k=int(args.top_k),
|
| 2204 |
+
)
|
| 2205 |
+
methodology = BenchmarkMethodologyConfig(
|
| 2206 |
+
num_samples=args.num_samples,
|
| 2207 |
+
min_generation_turns_per_config=int(args.min_generation_turns_per_config),
|
| 2208 |
+
min_warmup_generation_turns=int(args.min_warmup_generation_turns),
|
| 2209 |
+
runs_per_config=int(args.runs_per_config),
|
| 2210 |
+
)
|
| 2211 |
+
deployment_sweep = DeploymentSweep(
|
| 2212 |
+
include_baseline=not args.skip_baseline,
|
| 2213 |
+
spec_modes=tuple(args.spec_modes),
|
| 2214 |
+
mtp_num_steps=tuple(args.mtp_num_steps),
|
| 2215 |
+
dflash_draft_model=args.dflash_draft_model,
|
| 2216 |
+
dflash_block_sizes=tuple(args.dflash_block_sizes),
|
| 2217 |
+
)
|
| 2218 |
+
|
| 2219 |
+
if sampling.temperature < 0.0:
|
| 2220 |
+
raise RuntimeError(f"--temperature must be >= 0, got {sampling.temperature}.")
|
| 2221 |
+
if not (0.0 < sampling.top_p <= 1.0):
|
| 2222 |
+
raise RuntimeError(f"--top-p must be in (0, 1], got {sampling.top_p}.")
|
| 2223 |
+
if sampling.top_k == 0 or sampling.top_k < -1:
|
| 2224 |
+
raise RuntimeError(
|
| 2225 |
+
f"--top-k must be -1 (all vocab) or >= 1, got {sampling.top_k}."
|
| 2226 |
+
)
|
| 2227 |
+
if methodology.num_samples is not None and methodology.num_samples <= 0:
|
| 2228 |
+
raise RuntimeError(f"--num-samples must be > 0, got {methodology.num_samples}.")
|
| 2229 |
+
if methodology.runs_per_config <= 0:
|
| 2230 |
+
raise RuntimeError(
|
| 2231 |
+
f"--runs-per-config must be > 0, got {methodology.runs_per_config}."
|
| 2232 |
+
)
|
| 2233 |
+
if (
|
| 2234 |
+
methodology.min_generation_turns_per_config < 0
|
| 2235 |
+
or methodology.min_warmup_generation_turns < 0
|
| 2236 |
+
):
|
| 2237 |
+
raise RuntimeError(
|
| 2238 |
+
"--min-generation-turns-per-config and "
|
| 2239 |
+
"--min-warmup-generation-turns must be >= 0."
|
| 2240 |
+
)
|
| 2241 |
+
|
| 2242 |
+
try:
|
| 2243 |
+
concurrencies = _parse_int_csv(args.concurrencies)
|
| 2244 |
+
except ValueError as exc:
|
| 2245 |
+
raise RuntimeError("--concurrencies must be comma-separated integers.") from exc
|
| 2246 |
+
if not concurrencies:
|
| 2247 |
+
raise RuntimeError("No concurrencies specified.")
|
| 2248 |
+
if any(c < 1 for c in concurrencies):
|
| 2249 |
+
raise RuntimeError(
|
| 2250 |
+
f"--concurrencies values must be >= 1, got {concurrencies}."
|
| 2251 |
+
)
|
| 2252 |
+
duplicate_concurrencies = _duplicate_values([str(c) for c in concurrencies])
|
| 2253 |
+
if duplicate_concurrencies:
|
| 2254 |
+
raise RuntimeError(
|
| 2255 |
+
"Duplicate concurrencies: " + ",".join(duplicate_concurrencies)
|
| 2256 |
+
)
|
| 2257 |
+
|
| 2258 |
+
return SweepConfig(
|
| 2259 |
+
target_model=args.target_model,
|
| 2260 |
+
dflash_draft_model=args.dflash_draft_model,
|
| 2261 |
+
workloads=tuple(args.workloads),
|
| 2262 |
+
concurrencies=tuple(concurrencies),
|
| 2263 |
+
sampling=sampling,
|
| 2264 |
+
methodology=methodology,
|
| 2265 |
+
deployment_sweep=deployment_sweep,
|
| 2266 |
+
csv_output=args.csv_output,
|
| 2267 |
+
)
|
| 2268 |
+
|
| 2269 |
+
|
| 2270 |
+
def _get_current_cuda_runtime() -> tuple[int, int]:
|
| 2271 |
+
import torch
|
| 2272 |
+
from sglang.srt.utils import get_device_sm
|
| 2273 |
+
|
| 2274 |
+
if not torch.cuda.is_available():
|
| 2275 |
+
raise RuntimeError("CUDA is required for this sweep.")
|
| 2276 |
+
return int(torch.cuda.device_count()), int(get_device_sm())
|
| 2277 |
+
|
| 2278 |
+
|
| 2279 |
+
def build_shared_configs_for_runtime(
|
| 2280 |
+
sweep_config: SweepConfig,
|
| 2281 |
+
) -> tuple[list[SharedServerConfig], int]:
|
| 2282 |
+
visible_gpus, device_sm = _get_current_cuda_runtime()
|
| 2283 |
+
shared_configs = _build_shared_server_configs(
|
| 2284 |
+
device_sm=device_sm,
|
| 2285 |
+
visible_gpus=visible_gpus,
|
| 2286 |
+
max_concurrency=max(sweep_config.concurrencies),
|
| 2287 |
+
)
|
| 2288 |
+
return shared_configs, device_sm
|
| 2289 |
+
|
| 2290 |
+
|
| 2291 |
+
def build_shared_configs_for_modal(
|
| 2292 |
+
sweep_config: SweepConfig,
|
| 2293 |
+
*,
|
| 2294 |
+
device_sm: int,
|
| 2295 |
+
visible_gpus: int,
|
| 2296 |
+
) -> list[SharedServerConfig]:
|
| 2297 |
+
return _build_shared_server_configs(
|
| 2298 |
+
device_sm=int(device_sm),
|
| 2299 |
+
visible_gpus=int(visible_gpus),
|
| 2300 |
+
max_concurrency=max(sweep_config.concurrencies),
|
| 2301 |
+
)
|
| 2302 |
+
|
| 2303 |
+
|
| 2304 |
+
def build_benchmark_jobs(
|
| 2305 |
+
sweep_config: SweepConfig, shared_configs: list[SharedServerConfig]
|
| 2306 |
+
) -> list[BenchmarkJob]:
|
| 2307 |
+
return _build_benchmark_jobs(sweep_config, shared_configs)
|
| 2308 |
+
|
| 2309 |
+
|
| 2310 |
+
def run_benchmark_job_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
| 2311 |
+
job = benchmark_job_from_payload(payload)
|
| 2312 |
+
return job_outcome_to_payload(_run_benchmark_job_gracefully(job))
|
| 2313 |
+
|
| 2314 |
+
|
| 2315 |
+
def aggregate_job_outcomes(
|
| 2316 |
+
outcomes: list[JobResult | JobFailure],
|
| 2317 |
+
) -> list[ConfigResult]:
|
| 2318 |
+
return _aggregate_job_results(outcomes)
|
| 2319 |
+
|
| 2320 |
+
|
| 2321 |
+
def render_results(
|
| 2322 |
+
*,
|
| 2323 |
+
sweep_config: SweepConfig,
|
| 2324 |
+
shared_configs: list[SharedServerConfig],
|
| 2325 |
+
device_sm: int,
|
| 2326 |
+
config_results: list[ConfigResult],
|
| 2327 |
+
) -> list[dict[str, object]]:
|
| 2328 |
+
mode_keys = sweep_config.deployment_sweep.mode_keys
|
| 2329 |
+
for workload in sweep_config.workloads:
|
| 2330 |
+
workload_results = [
|
| 2331 |
+
result for result in config_results if result.key.workload == workload
|
| 2332 |
+
]
|
| 2333 |
+
if not workload_results:
|
| 2334 |
+
continue
|
| 2335 |
+
workload_shared_configs: list[SharedServerConfig] = []
|
| 2336 |
+
seen_shared_configs: set[SharedServerConfig] = set()
|
| 2337 |
+
for result in workload_results:
|
| 2338 |
+
shared_config = result.deployment.shared_config
|
| 2339 |
+
if shared_config not in seen_shared_configs:
|
| 2340 |
+
seen_shared_configs.add(shared_config)
|
| 2341 |
+
workload_shared_configs.append(shared_config)
|
| 2342 |
+
attention_backends = sorted(
|
| 2343 |
+
{config.attention_backend for config in workload_shared_configs}
|
| 2344 |
+
)
|
| 2345 |
+
tp_sizes = sorted({config.tp_size for config in workload_shared_configs})
|
| 2346 |
+
source_sample_count = next(
|
| 2347 |
+
(
|
| 2348 |
+
result.source_sample_count
|
| 2349 |
+
for result in workload_results
|
| 2350 |
+
if result.source_sample_count is not None
|
| 2351 |
+
),
|
| 2352 |
+
None,
|
| 2353 |
+
)
|
| 2354 |
+
source_generation_turn_count = next(
|
| 2355 |
+
(
|
| 2356 |
+
result.source_generation_turn_count
|
| 2357 |
+
for result in workload_results
|
| 2358 |
+
if result.source_generation_turn_count is not None
|
| 2359 |
+
),
|
| 2360 |
+
None,
|
| 2361 |
+
)
|
| 2362 |
+
print(f"\n\n##### Workload Summary: {workload} #####")
|
| 2363 |
+
_print_summary(
|
| 2364 |
+
config=sweep_config,
|
| 2365 |
+
workload=workload,
|
| 2366 |
+
config_results=workload_results,
|
| 2367 |
+
shared_configs=workload_shared_configs,
|
| 2368 |
+
attention_backends=attention_backends,
|
| 2369 |
+
tp_sizes=tp_sizes,
|
| 2370 |
+
concurrencies=list(sweep_config.concurrencies),
|
| 2371 |
+
device_sm=device_sm,
|
| 2372 |
+
mode_keys=mode_keys,
|
| 2373 |
+
source_sample_count=source_sample_count,
|
| 2374 |
+
source_generation_turn_count=source_generation_turn_count,
|
| 2375 |
+
results=_metric_map_from_config_results(workload_results),
|
| 2376 |
+
)
|
| 2377 |
+
|
| 2378 |
+
csv_rows = _build_csv_rows(config_results=config_results)
|
| 2379 |
+
_print_csv_summary(csv_rows)
|
| 2380 |
+
if sweep_config.csv_output is not None:
|
| 2381 |
+
_write_csv_summary(sweep_config.csv_output, csv_rows)
|
| 2382 |
+
return csv_rows
|
| 2383 |
+
|
| 2384 |
+
|
| 2385 |
+
def run_local_sweep(sweep_config: SweepConfig) -> list[ConfigResult]:
|
| 2386 |
+
shared_configs, device_sm = build_shared_configs_for_runtime(sweep_config)
|
| 2387 |
+
jobs = build_benchmark_jobs(sweep_config, shared_configs)
|
| 2388 |
+
job_results = [_run_benchmark_job_gracefully(job) for job in jobs]
|
| 2389 |
+
config_results = aggregate_job_outcomes(job_results)
|
| 2390 |
+
render_results(
|
| 2391 |
+
sweep_config=sweep_config,
|
| 2392 |
+
shared_configs=shared_configs,
|
| 2393 |
+
device_sm=device_sm,
|
| 2394 |
+
config_results=config_results,
|
| 2395 |
+
)
|
| 2396 |
+
return config_results
|
| 2397 |
+
|
| 2398 |
+
|
| 2399 |
+
def main() -> None:
|
| 2400 |
+
args = parse_args()
|
| 2401 |
+
sweep_config = build_sweep_config_from_args(args)
|
| 2402 |
+
run_local_sweep(sweep_config)
|
| 2403 |
+
|
| 2404 |
+
|
| 2405 |
+
if __name__ == "__main__":
|
| 2406 |
+
main()
|
benchmark/run_modal_benchmark.py
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Modal entrypoint for running benchmark jobs in parallel."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import importlib
|
| 6 |
+
import json
|
| 7 |
+
import sys
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from typing import Any
|
| 10 |
+
|
| 11 |
+
import modal
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
MINUTES = 60
|
| 15 |
+
HOURS = 60 * MINUTES
|
| 16 |
+
|
| 17 |
+
APP_NAME = "qwen35-397b-a17b-dflash-eval"
|
| 18 |
+
SGLANG_IMAGE = "lmsysorg/sglang:v0.5.13-cu130"
|
| 19 |
+
SGLANG_GIT_SHA = "ec36dde58083aca8f26c3740332498a11a06debf"
|
| 20 |
+
|
| 21 |
+
GPU_TYPE = "B200"
|
| 22 |
+
N_GPUS = 8
|
| 23 |
+
GPU = f"{GPU_TYPE}:{N_GPUS}"
|
| 24 |
+
CLOUD: str | None = "csc"
|
| 25 |
+
MAX_CONTAINERS = 32
|
| 26 |
+
JOB_TIMEOUT_S = 24 * HOURS
|
| 27 |
+
STARTUP_TIMEOUT_S = 30 * MINUTES
|
| 28 |
+
|
| 29 |
+
MODAL_VISIBLE_GPUS = N_GPUS
|
| 30 |
+
MODAL_DEVICE_SM = 100
|
| 31 |
+
|
| 32 |
+
HF_CACHE_PATH = "/root/.cache/huggingface"
|
| 33 |
+
FLASHINFER_CACHE_PATH = "/root/.cache/flashinfer"
|
| 34 |
+
EVAL_CACHE_PATH = "/cache/eval"
|
| 35 |
+
REMOTE_BENCHMARK_PATH = "/root/run_benchmark.py"
|
| 36 |
+
REMOTE_PATCH_DIR = "/root/patches"
|
| 37 |
+
REMOTE_PATCH_SCRIPT_PATH = "/root/apply_modal_patches.py"
|
| 38 |
+
|
| 39 |
+
LOCAL_BENCHMARK_PATH = Path(__file__).resolve().parent / "run_benchmark.py"
|
| 40 |
+
LOCAL_PATCH_DIR = Path(__file__).resolve().parent / "patches"
|
| 41 |
+
LOCAL_PATCH_SCRIPT_PATH = LOCAL_PATCH_DIR / "apply_patches.py"
|
| 42 |
+
FLASHINFER_PATCH_PATH = LOCAL_PATCH_DIR / "flashinfer-pr-3312.patch"
|
| 43 |
+
|
| 44 |
+
HF_CACHE_VOL = modal.Volume.from_name("huggingface-cache", create_if_missing=True)
|
| 45 |
+
FLASHINFER_CACHE_VOL = modal.Volume.from_name(
|
| 46 |
+
"flashinfer-cache", create_if_missing=True
|
| 47 |
+
)
|
| 48 |
+
EVAL_CACHE_VOL = modal.Volume.from_name(
|
| 49 |
+
"qwen35-dflash-eval-cache", create_if_missing=True
|
| 50 |
+
)
|
| 51 |
+
HF_SECRET = modal.Secret.from_name("huggingface-secret")
|
| 52 |
+
|
| 53 |
+
RUNTIME_ENV = {
|
| 54 |
+
"HF_HOME": HF_CACHE_PATH,
|
| 55 |
+
"HF_HUB_CACHE": HF_CACHE_PATH,
|
| 56 |
+
"HF_DATASETS_CACHE": f"{HF_CACHE_PATH}/datasets",
|
| 57 |
+
"TRANSFORMERS_CACHE": HF_CACHE_PATH,
|
| 58 |
+
"XDG_CACHE_HOME": f"{HF_CACHE_PATH}/xdg",
|
| 59 |
+
"HF_XET_HIGH_PERFORMANCE": "1",
|
| 60 |
+
"PYTHONPATH": "/root",
|
| 61 |
+
"SGLANG_PYSPY_DUMP_BEFORE_CRASH": "0",
|
| 62 |
+
"SGLANG_CUDA_COREDUMP_BEFORE_CRASH": "0",
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
sglang_image = (
|
| 66 |
+
modal.Image.from_registry(SGLANG_IMAGE)
|
| 67 |
+
.entrypoint([])
|
| 68 |
+
.add_local_file(
|
| 69 |
+
FLASHINFER_PATCH_PATH,
|
| 70 |
+
remote_path=f"{REMOTE_PATCH_DIR}/flashinfer-pr-3312.patch",
|
| 71 |
+
copy=True,
|
| 72 |
+
)
|
| 73 |
+
.add_local_file(
|
| 74 |
+
LOCAL_PATCH_SCRIPT_PATH,
|
| 75 |
+
remote_path=REMOTE_PATCH_SCRIPT_PATH,
|
| 76 |
+
copy=True,
|
| 77 |
+
)
|
| 78 |
+
.env(
|
| 79 |
+
{
|
| 80 |
+
"PYTHONPATH": "/root",
|
| 81 |
+
}
|
| 82 |
+
)
|
| 83 |
+
.uv_pip_install(
|
| 84 |
+
f"git+https://github.com/sgl-project/sglang.git@{SGLANG_GIT_SHA}#subdirectory=python",
|
| 85 |
+
"datasets",
|
| 86 |
+
"hf_xet",
|
| 87 |
+
"requests",
|
| 88 |
+
"transformers",
|
| 89 |
+
)
|
| 90 |
+
.run_commands(f"python {REMOTE_PATCH_SCRIPT_PATH}")
|
| 91 |
+
.run_commands(f"rm -rf {HF_CACHE_PATH}")
|
| 92 |
+
.add_local_file(LOCAL_BENCHMARK_PATH, remote_path=REMOTE_BENCHMARK_PATH)
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
app = modal.App(name=APP_NAME)
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def _load_benchmark_module():
|
| 99 |
+
return importlib.import_module("run_benchmark")
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
@app.function(
|
| 103 |
+
image=sglang_image,
|
| 104 |
+
gpu=GPU,
|
| 105 |
+
env=RUNTIME_ENV,
|
| 106 |
+
secrets=[HF_SECRET],
|
| 107 |
+
volumes={
|
| 108 |
+
HF_CACHE_PATH: HF_CACHE_VOL,
|
| 109 |
+
FLASHINFER_CACHE_PATH: FLASHINFER_CACHE_VOL,
|
| 110 |
+
EVAL_CACHE_PATH: EVAL_CACHE_VOL,
|
| 111 |
+
},
|
| 112 |
+
cloud=CLOUD,
|
| 113 |
+
retries=0,
|
| 114 |
+
timeout=JOB_TIMEOUT_S,
|
| 115 |
+
startup_timeout=STARTUP_TIMEOUT_S,
|
| 116 |
+
max_containers=MAX_CONTAINERS,
|
| 117 |
+
single_use_containers=True,
|
| 118 |
+
)
|
| 119 |
+
def run_benchmark_job_modal(job_payload: dict[str, Any]) -> dict[str, Any]:
|
| 120 |
+
sys.path.insert(0, str(Path(REMOTE_BENCHMARK_PATH).parent))
|
| 121 |
+
benchmark = _load_benchmark_module()
|
| 122 |
+
benchmark.CACHE_DIR = Path(EVAL_CACHE_PATH)
|
| 123 |
+
return benchmark.run_benchmark_job_payload(job_payload)
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def _platform_failure_payload(
|
| 127 |
+
job_payload: dict[str, Any],
|
| 128 |
+
exc: BaseException | str,
|
| 129 |
+
) -> dict[str, Any]:
|
| 130 |
+
benchmark = _load_benchmark_module()
|
| 131 |
+
job = benchmark.benchmark_job_from_payload(job_payload)
|
| 132 |
+
if isinstance(exc, BaseException):
|
| 133 |
+
error_type = type(exc).__name__
|
| 134 |
+
error_message = f"Modal platform failure: {exc!r}"
|
| 135 |
+
else:
|
| 136 |
+
error_type = "UnexpectedModalResult"
|
| 137 |
+
error_message = exc
|
| 138 |
+
return benchmark.job_outcome_to_payload(
|
| 139 |
+
benchmark.JobFailure(
|
| 140 |
+
key=job.key,
|
| 141 |
+
deployment=job.deployment,
|
| 142 |
+
run_index=job.run_index,
|
| 143 |
+
error_type=error_type,
|
| 144 |
+
error_message=error_message,
|
| 145 |
+
)
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def _write_raw_output(path: str, outcome_payloads: list[dict[str, Any]]) -> None:
|
| 150 |
+
output_path = Path(path)
|
| 151 |
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
| 152 |
+
with open(output_path, "w") as f:
|
| 153 |
+
for payload in outcome_payloads:
|
| 154 |
+
f.write(json.dumps(payload, sort_keys=True) + "\n")
|
| 155 |
+
print(
|
| 156 |
+
f"[modal] wrote {len(outcome_payloads)} raw job outcomes to {output_path}",
|
| 157 |
+
flush=True,
|
| 158 |
+
)
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def _build_eval_argv(
|
| 162 |
+
*,
|
| 163 |
+
workloads: str,
|
| 164 |
+
csv_output: str,
|
| 165 |
+
target_model: str,
|
| 166 |
+
dflash_draft_model: str,
|
| 167 |
+
spec_modes: str,
|
| 168 |
+
mtp_num_steps: str,
|
| 169 |
+
skip_baseline: bool,
|
| 170 |
+
enable_thinking: bool,
|
| 171 |
+
max_new_tokens: int,
|
| 172 |
+
temperature: float,
|
| 173 |
+
top_p: float,
|
| 174 |
+
top_k: int,
|
| 175 |
+
concurrencies: str,
|
| 176 |
+
num_samples: int,
|
| 177 |
+
runs_per_config: int,
|
| 178 |
+
min_generation_turns_per_config: int,
|
| 179 |
+
min_warmup_generation_turns: int,
|
| 180 |
+
dflash_block_sizes: str,
|
| 181 |
+
) -> list[str]:
|
| 182 |
+
argv = [
|
| 183 |
+
"--workloads",
|
| 184 |
+
workloads,
|
| 185 |
+
"--target-model",
|
| 186 |
+
target_model,
|
| 187 |
+
"--spec-modes",
|
| 188 |
+
spec_modes,
|
| 189 |
+
"--mtp-num-steps",
|
| 190 |
+
mtp_num_steps,
|
| 191 |
+
"--max-new-tokens",
|
| 192 |
+
str(int(max_new_tokens)),
|
| 193 |
+
"--temperature",
|
| 194 |
+
str(float(temperature)),
|
| 195 |
+
"--top-p",
|
| 196 |
+
str(float(top_p)),
|
| 197 |
+
"--top-k",
|
| 198 |
+
str(int(top_k)),
|
| 199 |
+
"--concurrencies",
|
| 200 |
+
concurrencies,
|
| 201 |
+
"--runs-per-config",
|
| 202 |
+
str(int(runs_per_config)),
|
| 203 |
+
"--min-generation-turns-per-config",
|
| 204 |
+
str(int(min_generation_turns_per_config)),
|
| 205 |
+
"--min-warmup-generation-turns",
|
| 206 |
+
str(int(min_warmup_generation_turns)),
|
| 207 |
+
"--dflash-block-sizes",
|
| 208 |
+
dflash_block_sizes,
|
| 209 |
+
]
|
| 210 |
+
if csv_output:
|
| 211 |
+
argv.extend(["--csv-output", csv_output])
|
| 212 |
+
if dflash_draft_model:
|
| 213 |
+
argv.extend(["--dflash-draft-model", dflash_draft_model])
|
| 214 |
+
if skip_baseline:
|
| 215 |
+
argv.append("--skip-baseline")
|
| 216 |
+
if enable_thinking:
|
| 217 |
+
argv.append("--enable-thinking")
|
| 218 |
+
else:
|
| 219 |
+
argv.append("--disable-thinking")
|
| 220 |
+
if num_samples > 0:
|
| 221 |
+
argv.extend(["--num-samples", str(int(num_samples))])
|
| 222 |
+
return argv
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
@app.local_entrypoint()
|
| 226 |
+
def main(
|
| 227 |
+
workloads: str = "gsm8k",
|
| 228 |
+
csv_output: str = "",
|
| 229 |
+
target_model: str = "Qwen/Qwen3.5-397B-A17B",
|
| 230 |
+
dflash_draft_model: str = "",
|
| 231 |
+
spec_modes: str = "mtp",
|
| 232 |
+
mtp_num_steps: str = "3",
|
| 233 |
+
skip_baseline: bool = False,
|
| 234 |
+
enable_thinking: bool = True,
|
| 235 |
+
max_new_tokens: int = 4096,
|
| 236 |
+
temperature: float = 0.0,
|
| 237 |
+
top_p: float = 1.0,
|
| 238 |
+
top_k: int = 1,
|
| 239 |
+
concurrencies: str = "1,32",
|
| 240 |
+
num_samples: int = 0,
|
| 241 |
+
runs_per_config: int = 1,
|
| 242 |
+
min_generation_turns_per_config: int = 1024,
|
| 243 |
+
min_warmup_generation_turns: int = 8,
|
| 244 |
+
dflash_block_sizes: str = "default",
|
| 245 |
+
raw_output: str = "",
|
| 246 |
+
device_sm: int = MODAL_DEVICE_SM,
|
| 247 |
+
visible_gpus: int = MODAL_VISIBLE_GPUS,
|
| 248 |
+
) -> None:
|
| 249 |
+
benchmark = _load_benchmark_module()
|
| 250 |
+
args = benchmark.parse_args(
|
| 251 |
+
_build_eval_argv(
|
| 252 |
+
workloads=workloads,
|
| 253 |
+
csv_output=csv_output,
|
| 254 |
+
target_model=target_model,
|
| 255 |
+
dflash_draft_model=dflash_draft_model,
|
| 256 |
+
spec_modes=spec_modes,
|
| 257 |
+
mtp_num_steps=mtp_num_steps,
|
| 258 |
+
skip_baseline=skip_baseline,
|
| 259 |
+
enable_thinking=enable_thinking,
|
| 260 |
+
max_new_tokens=max_new_tokens,
|
| 261 |
+
temperature=temperature,
|
| 262 |
+
top_p=top_p,
|
| 263 |
+
top_k=top_k,
|
| 264 |
+
concurrencies=concurrencies,
|
| 265 |
+
num_samples=num_samples,
|
| 266 |
+
runs_per_config=runs_per_config,
|
| 267 |
+
min_generation_turns_per_config=min_generation_turns_per_config,
|
| 268 |
+
min_warmup_generation_turns=min_warmup_generation_turns,
|
| 269 |
+
dflash_block_sizes=dflash_block_sizes,
|
| 270 |
+
)
|
| 271 |
+
)
|
| 272 |
+
sweep_config = benchmark.build_sweep_config_from_args(args)
|
| 273 |
+
shared_configs = benchmark.build_shared_configs_for_modal(
|
| 274 |
+
sweep_config,
|
| 275 |
+
device_sm=device_sm,
|
| 276 |
+
visible_gpus=visible_gpus,
|
| 277 |
+
)
|
| 278 |
+
jobs = benchmark.build_benchmark_jobs(sweep_config, shared_configs)
|
| 279 |
+
job_payloads = [benchmark.benchmark_job_to_payload(job) for job in jobs]
|
| 280 |
+
|
| 281 |
+
print(
|
| 282 |
+
f"[modal] dispatching {len(job_payloads)} benchmark jobs "
|
| 283 |
+
f"across up to {MAX_CONTAINERS} containers"
|
| 284 |
+
)
|
| 285 |
+
mapped_outputs = list(
|
| 286 |
+
run_benchmark_job_modal.map(
|
| 287 |
+
job_payloads,
|
| 288 |
+
order_outputs=True,
|
| 289 |
+
return_exceptions=True,
|
| 290 |
+
)
|
| 291 |
+
)
|
| 292 |
+
|
| 293 |
+
outcome_payloads: list[dict[str, Any]] = []
|
| 294 |
+
for job_payload, output in zip(job_payloads, mapped_outputs):
|
| 295 |
+
if isinstance(output, BaseException):
|
| 296 |
+
outcome_payloads.append(_platform_failure_payload(job_payload, output))
|
| 297 |
+
elif not isinstance(output, dict):
|
| 298 |
+
outcome_payloads.append(
|
| 299 |
+
_platform_failure_payload(
|
| 300 |
+
job_payload,
|
| 301 |
+
f"Modal returned {type(output).__name__}, expected dict payload.",
|
| 302 |
+
)
|
| 303 |
+
)
|
| 304 |
+
else:
|
| 305 |
+
outcome_payloads.append(output)
|
| 306 |
+
|
| 307 |
+
if raw_output:
|
| 308 |
+
_write_raw_output(raw_output, outcome_payloads)
|
| 309 |
+
|
| 310 |
+
outcomes = [
|
| 311 |
+
benchmark.job_outcome_from_payload(payload) for payload in outcome_payloads
|
| 312 |
+
]
|
| 313 |
+
config_results = benchmark.aggregate_job_outcomes(outcomes)
|
| 314 |
+
benchmark.render_results(
|
| 315 |
+
sweep_config=sweep_config,
|
| 316 |
+
shared_configs=shared_configs,
|
| 317 |
+
device_sm=device_sm,
|
| 318 |
+
config_results=config_results,
|
| 319 |
+
)
|
| 320 |
+
sys.stdout.flush()
|
| 321 |
+
sys.stderr.flush()
|
config.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"DFlashDraftModel"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"auto_map": {
|
| 8 |
+
"AutoModel": "dflash.DFlashDraftModel"
|
| 9 |
+
},
|
| 10 |
+
"bos_token_id": null,
|
| 11 |
+
"dflash_config": {
|
| 12 |
+
"block_size": 16,
|
| 13 |
+
"mask_token_id": 248077,
|
| 14 |
+
"target_layer_ids": [
|
| 15 |
+
1,
|
| 16 |
+
9,
|
| 17 |
+
17,
|
| 18 |
+
25,
|
| 19 |
+
33,
|
| 20 |
+
41,
|
| 21 |
+
49,
|
| 22 |
+
57
|
| 23 |
+
]
|
| 24 |
+
},
|
| 25 |
+
"dtype": "bfloat16",
|
| 26 |
+
"eos_token_id": 248044,
|
| 27 |
+
"head_dim": 128,
|
| 28 |
+
"hidden_act": "silu",
|
| 29 |
+
"hidden_size": 4096,
|
| 30 |
+
"initializer_range": 0.02,
|
| 31 |
+
"intermediate_size": 12288,
|
| 32 |
+
"layer_types": [
|
| 33 |
+
"sliding_attention",
|
| 34 |
+
"sliding_attention",
|
| 35 |
+
"sliding_attention",
|
| 36 |
+
"sliding_attention",
|
| 37 |
+
"sliding_attention",
|
| 38 |
+
"full_attention"
|
| 39 |
+
],
|
| 40 |
+
"max_position_embeddings": 262144,
|
| 41 |
+
"max_window_layers": 6,
|
| 42 |
+
"model_type": "qwen3",
|
| 43 |
+
"num_attention_heads": 32,
|
| 44 |
+
"num_hidden_layers": 6,
|
| 45 |
+
"num_key_value_heads": 8,
|
| 46 |
+
"num_target_layers": 60,
|
| 47 |
+
"pad_token_id": null,
|
| 48 |
+
"rms_norm_eps": 1e-06,
|
| 49 |
+
"rope_parameters": {
|
| 50 |
+
"rope_theta": 10000000,
|
| 51 |
+
"rope_type": "default"
|
| 52 |
+
},
|
| 53 |
+
"sliding_window": 4096,
|
| 54 |
+
"tie_word_embeddings": false,
|
| 55 |
+
"transformers_version": "5.7.0",
|
| 56 |
+
"use_cache": true,
|
| 57 |
+
"use_sliding_window": true,
|
| 58 |
+
"vocab_size": 248320
|
| 59 |
+
}
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:63e59f09a49d0c6101305a8ce8b4f29435941c9ff76186fdfea047cbb40c5a37
|
| 3 |
+
size 6652291584
|