网上找了一圈 性能评测工具,很多都要 自己把模型拉起来,还动不动就想去HuggingFace下载,都不太好用。考虑到目前不管是开源还是闭源,各大模型的推理服务,基本都遵循 OpenAI 的 API 接口。所以针对该接口编写了一个简单的脚本: simple-bench-to-api.py ,放到了之前的这篇文章中:单卡4090上部署的DeepSeek-R1小模型的并发性能 已有50多个用户付费使用。随着近2个多月自己的使用和付费用户的反馈,做了如下更新:
- 支持将 max_tokens
设置为逗号分隔的多个。这样想测试不同上下文长度 下的不同并发性能,设置好一把就跑完了。结果会针对每一个
max_tokens 值,输出一个 Markdown 表格。
-
改为全异步模式,优化了异常中断时的错误处理逻辑。
-
支持设置模型自带的 tokenizer 分词器,这样token数的计算会更加准确。
完整参数说明:
-
--url API基础URL地址,例如:http://localhost:8000/v1
-
--model 模型名称, 例如 qwen3:32b;需要和 url 对应的
/v1/models 接口返回的模型名称列表中的某一个匹配。
-
--tokenizer_path 模型tokenizer的路径,可选。
-
--concurrencys 逗号分隔的并发数
-
--concurrency 单个并发数,如果设置了,则忽略concurrencys参数
-
--prompt 压测请求的提示词,默认值: "Once upon a time"
-
--max_tokens
逗号分隔的最大token数 -
--api_key API密钥,无默认值
-
--duration_seconds 每个并发值下的压测持续时间(秒),默认10秒
可以通过 python simple-bench-to-api.py -h 查看参数列表。
压测命令样例:
nohup python3 -u simple-bench-to-api.py \
--url http://10.96.0.180:7869/v1 \
--model qwen3 \
--tokenizer_path /data/ai/models/qwen/Qwen3-32B-AWQ \
--concurrencys 1,10,30,60,100 \
--prompt "Introduce the history of China" \
--max_tokens 1024,8192,16384 \
--api_key sk-xxx \
--duration_seconds 30 \
> result-qwen3-32b-awq.log 2>&1 &
tail -f result-qwen3-32b-awq.log
这个命令会发起压测,后台执行,并把压测结果输出到文件 result-qwen3-32b-awq.log, 并跟踪文件内容。所以执行之后就可以去喝茶了。
结果样例:
----- max_tokens=16384 压测结果汇总 -----
| 指标 \ 并发数 | 1个并发 | 10个并发 | 30个并发 | 60个并发 | 100个并发 |
| --- | --- | --- | --- | --- | --- |
| 总请求数 | 2 | 10 | 30 | 60 | 100 |
| 成功率 | 100.00% | 100.00% | 100.00% | 100.00% | 100.00% |
| 平均延迟 | 30.8722s | 58.5037s | 82.8571s | 150.1731s | 245.7002s |
| 最大延迟 | 33.9639s | 66.3691s | 99.2804s | 193.0228s | 345.9153s |
| 最小延迟 | 27.7806s | 49.2646s | 64.5804s | 87.4991s | 117.8750s |
| P90延迟 | 33.3456s | 62.9588s | 93.6751s | 185.0530s | 334.1519s |
| P95延迟 | 33.6548s | 64.6640s | 97.6318s | 189.0291s | 341.7791s |
| P99延迟 | 33.9021s | 66.0281s | 99.1890s | 192.0414s | 344.9784s |
| 平均首字延迟 | 0.0573s | 0.2743s | 0.7017s | 1.3150s | 2.2110s |
| 总生成tokens数 | 2908 | 12638 | 38174 | 72154 | 122569 |
| 单并发最小吞吐量 | 46.75 tokens/s | 19.43 tokens/s | 13.37 tokens/s | 5.41 tokens/s | 2.83 tokens/s |
| 单并发最大吞吐量 | 47.54 tokens/s | 24.63 tokens/s | 18.27 tokens/s | 10.58 tokens/s | 8.69 tokens/s |
| 单并发平均吞吐量 | 47.14 tokens/s | 21.62 tokens/s | 15.41 tokens/s | 8.28 tokens/s | 5.52 tokens/s |
| 总体吞吐量 | 47.07 tokens/s | 190.31 tokens/s | 384.15 tokens/s | 373.54 tokens/s | 354.11 tokens/s |
把这个文本粘贴到某个可以渲染Markdown的编辑器中,就可以看到类似如下的表格:
其中的几个概念:
- ”延迟“:从发出请求,到接收到最后一个token/字符的时间
- “P90延迟”:分位数90的延迟,计算方法为延迟从小到大排序,前90%的最大延迟值,和下一个延迟值,基于线性插值计算的一个介于2者之间的值。
- “首字延迟”:从发出请求,到接收到第一个返回字符的时间。
- “单并发吞吐量”的概念,是指站在每个并发用户(通道)的角度看,从首token返回后,token的生成速度。统计时间不包含首字延迟。即一个通道的吞吐量 = 该通道生成的token数/除首token延迟外的生成时间。个人觉得,这个指标加上平均首字延迟,能反映真实的用户体感。
具体指标的含义:
-
平均首字延迟:所有通道的首字延迟的平均值
-
单并发最小吞吐量: 所有并发通道中,吞吐量最小的通道的吞吐量
-
单并发最大吞吐量: 所有并发通道中,吞吐量最大的通道的吞吐量
-
单并发平均吞吐量:所有并发通道的吞吐量的平均值
-
总体吞吐量:在压测期间所有通道生成的tokens总数/压测开始到结束的时间
-
P90延迟: 表示有90%的请求延迟低于这个数值
-
P95延迟: 表示有95%的请求延迟低于这个数值
-
P99延迟:表示有95%的请求延迟低于这个数值
以下是 simple-bench-to-api.py 代码内容,需要的小伙伴可自行取用。可以在pc版微信中打开拷贝。