Benchmark
SGLang benchmark 用于比较不同 batch size 或服务并发下的延迟与吞吐。固定 batch 使用 bench_one_batch,HTTP 服务压测使用 bench_serving;kernel 时间线采集见 Profile。
1. 测量口径与环境
| 目标 | 工具 | 结果 |
|---|---|---|
| 固定 batch 的模型执行成本 | sglang.bench_one_batch | Prefill latency、median decode latency |
| 服务在不同并发下的表现 | sglang.bench_serving | TTFT、TPOT、ITL、端到端吞吐 |
Batch size 不等于客户端并发数。 --batch-size 32 表示一次固定 32 条序列的模型测试;--max-concurrency 32 限制 HTTP 在途请求数,实际 GPU batch 会随服务器调度变化。
在安装了 SGLang 的 GPU 机器上使用 Bash 执行。每个终端先设置以下变量:
export MODEL_PATH=/path/to/model
export CUDA_VISIBLE_DEVICES=0
export TP_SIZE=1
export INPUT_LEN=1024
export PORT=30000
export GRAPH_BS_FLAG=--cuda-graph-max-bs-decode
mkdir -p results替换模型路径,按可见 GPU 数量设置 TP。本文按本地 SGLang 95140a7b0c 核对;旧版若只有 --cuda-graph-max-bs,将 GRAPH_BS_FLAG 改成该名称。当前源码保留 sglang.bench_one_batch、sglang.bench_serving 兼容入口,对应新名称为 sglang.benchmark.one_batch、sglang.benchmark.serving。
2. 固定 Batch 测试
2.1 Prefill 与 10 步 Decode
先停止占用同组 GPU 的 server。以下命令只加载一次模型,固定输入长度,依次测量多个 batch size:
python3 -m sglang.bench_one_batch \
--model-path "$MODEL_PATH" \
--tp-size "$TP_SIZE" \
--mem-fraction-static 0.7 \
--batch-size 1 8 32 64 128 \
--input-len "$INPUT_LEN" \
--output-len 11 \
--run-name prefill_decode10 \
--result-filename results/batch_prefill_decode10.jsonl--output-len 是总输出 token 数。普通自回归解码中,prefill 生成首 token,随后执行 output_len - 1 次 decode,因此 1 次 prefill + 10 步 decode 对应 --output-len 11。只测试某个 batch 时,将列表改成一个整数,例如 --batch-size 32。
工具先预热,再分别输出每组的 prefill 和 decode 指标:
| 字段 | 含义 |
|---|---|
prefill_latency | 一次 prefill 的耗时,单位为秒。 |
prefill_throughput | 输入 token 吞吐,约为 BS × INPUT_LEN / prefill_latency。 |
median_decode_latency | Decode 单步耗时的中位数,单位为秒,不包含 prefill 时间。 |
median_decode_throughput | Decode 吞吐,约为 BS / median_decode_latency,单位为 token/s。 |
10 步适合日常检查和与 profile 对照;稳定性能比较可增加到 --output-len 129,测量 128 步 decode,并重复运行。上下文随 decode 增长,比较不同 batch 时应固定输入、输出长度。同步计时仍包含部分 CPU 发射开销,不等同于所有 GPU kernel 时间之和。
2.2 Prefill 单独测试
只测 prefill 时,将输出长度设为 1:
python3 -m sglang.bench_one_batch \
--model-path "$MODEL_PATH" \
--tp-size "$TP_SIZE" \
--mem-fraction-static 0.7 \
--batch-size 1 8 32 64 128 \
--input-len "$INPUT_LEN" \
--output-len 1 \
--run-name prefill_only \
--result-filename results/batch_prefill.jsonl单独分析 decode 指标时直接读取第 2.1 节的 median_decode_latency,保留前面的真实 prefill 来建立 KV Cache,无需构造虚假缓存。
3. Server 并发测试
3.1 服务启动
终端 A 启动服务,等待 ready。测量正式性能时不加 nsys:
python3 -m sglang.launch_server \
--model-path "$MODEL_PATH" \
--host 0.0.0.0 --port "$PORT" \
--tp-size "$TP_SIZE" \
--chunked-prefill-size -1 \
--disable-radix-cache \
--max-running-requests 128 \
"$GRAPH_BS_FLAG" 128 \
--mem-fraction-static 0.7这是一组便于分析的受控配置:
| 参数 | 作用 |
|---|---|
--chunked-prefill-size -1 | 关闭分块 prefill,便于观察完整 prefill;长输入可能增大临时显存需求,并拉长 decode 等待时间。 |
--disable-radix-cache | 关闭前缀缓存复用,避免重复输入减少 prefill 计算;decode 的 KV Cache 仍正常使用。 |
--max-running-requests 128 | 设置运行请求数上限,不会自动产生 128 并发。 |
"$GRAPH_BS_FLAG" 128 | 设置 decode CUDA Graph 的最大捕获 batch size,不会固定实际 batch size。 |
--mem-fraction-static 0.7 | 设置模型权重与 KV Cache 池的静态显存预算比例;不是进程总显存占用的硬上限。 |
评估线上配置时,应恢复线上实际使用的 chunked prefill、前缀缓存和显存预算。不同配置的结果不能直接混为一组。
3.2 Prefill 并发扫描
终端 B 执行以下命令,依次测量 1、8、32、64、128 并发。每个请求只生成 1 个 token,主要观察 TTFT 和输入 token 吞吐:
for C in 1 8 32 64 128; do
python3 -m sglang.bench_serving \
--backend sglang \
--host 127.0.0.1 --port "$PORT" \
--model "$MODEL_PATH" \
--dataset-name random \
--random-input-len "$INPUT_LEN" \
--random-output-len 1 \
--random-range-ratio 1 \
--request-rate inf \
--max-concurrency "$C" \
--num-prompts "$((C * 10))" \
--output-file "results/server_prefill_c${C}.jsonl"
done--request-rate inf 表示尽快发请求,并由 --max-concurrency 限制在途数量;请求完成后继续补充,直到达到 --num-prompts。--random-range-ratio 1 固定随机数据集的目标输入、输出长度;实际 token 数以测试报告为准。
输出 1 token 已包含 prefill 后的首 token 生成,但不会继续执行常规 decode 循环。TTFT 仍包含 HTTP、排队、调度和输出处理,因此它是 server prefill 场景的端到端指标,不是纯 GPU prefill 时间。此时 TPOT 没有比较意义。
3.3 Decode 并发扫描
保持输入长度不变,生成 128 个 token,主要观察 TPOT、ITL 和输出 token 吞吐:
for C in 1 8 32 64 128; do
python3 -m sglang.bench_serving \
--backend sglang \
--host 127.0.0.1 --port "$PORT" \
--model "$MODEL_PATH" \
--dataset-name random \
--random-input-len "$INPUT_LEN" \
--random-output-len 128 \
--random-range-ratio 1 \
--request-rate inf \
--max-concurrency "$C" \
--num-prompts "$((C * 10))" \
--output-file "results/server_decode_c${C}.jsonl"
doneTPOT 是首 token 之后的平均每 token 时间;ITL 是流式输出的相邻 token 间隔。该测试仍然执行 prefill,而且新请求的 prefill 可能影响正在进行的 decode。输出吞吐也按整个测试窗口统计,不能当成纯 decode kernel 吞吐。
客户端默认先执行预热,并默认忽略 EOS 以满足指定输出长度。示例请求量适合初步扫描;正式比较应增加请求数、重复运行,并保持模型、TP、上下文长度及服务参数一致。输出文件会追加结果,独立实验应使用新文件名。
4. 容量与结果检查
- 输入长度加输出长度必须符合模型上下文限制;batch 越大、序列越长,KV Cache 需求越高。先跑通小 batch,再增加到 128。
bench_one_batch可能跳过超出容量的配置,应确认每档 batch 都有结果,不能将缺失结果当成测试成功。- JSONL 结果按运行追加;独立实验使用新文件名。比较时记录模型、GPU、TP、长度、并发及 CUDA Graph 配置。
- 本地版本的
random数据集从 ShareGPT 采样并调整长度,首次使用可能下载数据;离线环境可在bench_serving中加--dataset-path /path/to/sharegpt.json。 - 正式延迟和吞吐使用不带 profiler 的测试结果。时间线定位方法见 Profile。