1. 压测前先想清楚:你到底要测什么
很多人部署完 Triton 之后,第一反应是“跑一下看看快不快”。但“快”是个很模糊的词。是单次请求快?还是高并发下整体吞吐高?还是 P99 延迟稳定?这三个问题的答案往往互相矛盾。
我见过太多团队上线前只测了一个并发数,看到吞吐数字还不错就放心了,结果生产环境一上量,P99 延迟直接飙到几百毫秒,用户端开始超时重试,雪崩就这么来的。
perf_analyzer 是 Triton 官方自带的性能压测工具,随 Triton Client SDK 一起分发。它能做的事比大多数人以为的多:固定并发压测、固定请求速率压测、扫描并发区间找吞吐天花板、输出延迟分位数报告、生成 CSV 供后续分析、支持 LLM 流式场景的 TTFT/TPOT 指标采集。
这篇文章面向的是已经有一个能跑起来的 Triton 服务、准备做上线前性能基线测试的读者。你需要知道模型名、输入 tensor 名称和形状、Triton 的 gRPC 端口(默认 8001)。如果你还没有可用的 Triton 服务,建议先把服务跑通再回来做压测。
整篇的节奏是:先讲清楚压测的目标和指标含义,然后给出可复制的命令和配置,接着演示一次完整的压测流程并解读结果,最后把常见的坑和排查方法列出来。你可以直接照着操作,也可以把命令改一改用到自己的模型上。
2. 环境准备与 TaoToken 接入前置
2.1 安装 perf_analyzer
perf_analyzer 有两种获取方式。第一种是 pip 安装 Triton Client SDK:
pip install tritonclient[all]安装完成后,perf_analyzer 可执行文件会在 Python 的 bin 目录下。如果提示找不到命令,检查一下 PATH 是否包含了 pip 的 bin 路径。
第二种方式是从 Triton 官方 Docker 镜像中直接使用,适合不想在宿主机装依赖的场景:
docker run --rm nvcr.io/nvidia/tritonserver:24.05-py3-sdk \ perf_analyzer --help这种方式的好处是版本和 Triton Server 对齐,避免客户端和服务端协议不匹配的问题。实测下来,如果你的 Triton Server 用的是 24.05,客户端也尽量用同版本。
2.2 确认 Triton 服务可达
在压测之前,先用简单的 curl 或 tritonclient 确认服务在跑:
curl -v localhost:8001/v2/health/ready返回 200 就说明 gRPC 端口正常。如果这一步不通,后面的压测命令全都会报连接错误。
2.3 关于 API 接入与模型调用
如果你在压测之外还需要做模型对话验证、coding plan 管理或者 API Key 的创建,可以走 TaoToken 的对应入口。压测本身是本地行为,不依赖外部 API,但如果你要对比线上服务的表现,可以先把本地基线跑出来,再用同样的输入去线上做对照。
模型对话入口:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model_chat
Coding Plan 入口:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding_plan
API Keys 管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api_keys
接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc
这些入口在后面的验证和排障环节会用到,先放在这里备查。
3. 可复制的 perf_analyzer 配置与命令
3.1 基础压测命令
假设你有一个名为 yolov8s 的模型,输入 tensor 叫 images,形状是 3x640x640,Triton 跑在 localhost:8001。最基础的固定并发压测命令如下:
perf_analyzer -m yolov8s \ --concurrency-range 4 \ --shape images:3,640,640 \ --input-data random \ -u localhost:8001 \ --measurement-interval 10000这条命令的含义是:以 4 个并发请求持续压测 10 秒,输入数据用随机数生成。输出会包含吞吐量(infer/sec)和延迟分位数。
3.2 关键参数速查
下面这张表把最常用的参数和它们的实际作用列出来,方便你按需组合:
| 参数 | 作用 | 示例 |
|---|---|---|
| -m | 模型名称 | -m yolov8s |
| --concurrency-range | 并发数范围,支持 start:end:step | --concurrency-range 1:16:2 |
| -b | Batch size | -b 4 |
| --shape | 输入 tensor 形状 | --shape images:3,640,640 |
| --input-data | 输入数据生成方式 | random / zero / file:data.json |
| -u | Triton 服务地址 | -u localhost:8001 |
| -i | 使用 gRPC streaming | -i grpc |
| --measurement-interval | 每次测量持续时间(ms) | --measurement-interval 10000 |
| --request-rate-range | 固定请求速率压测 | --request-rate-range 500:2000:500 |
| --percentile | 报告延迟分位数 | --percentile 95 |
| -f | 输出 CSV 报告 | -f results.csv |
| --async | 异步请求模式 | --async |
| --streaming | 流式输出模式(LLM) | --streaming |
3.3 扫描并发找吞吐天花板
单点压测只能告诉你“这个并发下表现如何”,但你想知道的是“最优并发是多少”。这时候用 concurrency-range 做扫描:
perf_analyzer -m yolov8s \ --concurrency-range 1:32:2 \ --shape images:3,640,640 \ --input-data random \ -u localhost:8001 \ --measurement-interval 15000 \ -f latency_vs_concurrency.csv这条命令会依次以 1、3、5、…、31 的并发数各压 15 秒,把结果写入 CSV。跑完之后你可以用 pandas 画图:
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv('latency_vs_concurrency.csv') fig, ax1 = plt.subplots() ax1.plot(df['Concurrency'], df['Throughput'], 'b-o', label='Throughput') ax1.set_xlabel('Concurrency') ax1.set_ylabel('Throughput (infer/sec)', color='b') ax2 = ax1.twinx() ax2.plot(df['Concurrency'], df['p99 Latency'], 'r-s', label='P99 Latency') ax2.set_ylabel('P99 Latency (us)', color='r') fig.legend() plt.show()你要找的是吞吐曲线开始变平缓的那个点。在那之前,增加并发能换来吞吐增长;在那之后,并发增加只会让延迟上升,吞吐几乎不变。
3.4 对比不同 batch size 和精度
如果你在选型阶段,可以用脚本批量对比:
for batch_size in 1 4 8 16; do for fp_mode in fp16 int8; do echo "=== Testing batch=$batch_size, $fp_mode ===" perf_analyzer -m resnet_${fp_mode} \ -b $batch_size \ --concurrency-range 8 \ --input-data random \ -f result_b${batch_size}_${fp_mode}.csv done done跑完之后对比各组的吞吐和 P99,就能找到性价比最高的组合。
3.5 LLM 流式模型的压测
LLM 的压测和传统模型不一样,因为输出是流式的,你关心的不只是总延迟,还有首 token 延迟(TTFT)和每 token 生成时间(TPOT):
perf_analyzer -m llama3 \ --input-data prompts.json \ --streaming \ --measurement-interval 30000 \ --concurrency-range 1:8:2 \ -u localhost:8001 \ --async \ --request-parameter max_tokens:256 \ --request-parameter temperature:0.0这里 prompts.json 需要你自己准备,格式是每行一个 JSON 对象,包含输入 tensor 的数据。--streaming 开启流式模式后,perf_analyzer 会额外报告 TTFT 和 TPOT。
4. 验证请求与结果解读
4.1 一次完整的压测输出
跑完上面的扫描命令后,你会看到类似这样的输出:
*** Measurement Settings *** Service kind: Triton Using GRPC service at localhost:8001 Measurement window: 15000 msec Request concurrency: 8 Request count: 128345 Throughput: 8556 infer/sec Avg latency: 0.934 ms (overhead 0.128 ms + queue 0.342 ms + compute 0.464 ms) Latency percentiles: p50: 0.921 ms p90: 1.152 ms p95: 1.284 ms p99: 1.623 ms4.2 怎么读这些数字
吞吐量 8556 infer/sec 是这个并发下的处理能力。但更值得看的是延迟的构成:
overhead 0.128ms 是网络和序列化开销,queue 0.342ms 是请求在队列里等待的时间,compute 0.464ms 是模型实际计算时间。如果 queue 占比明显大于 compute,说明请求排队是主要瓶颈,增加模型实例数会有帮助。如果 compute 占主导,那就要考虑优化 kernel 或者用更高精度的硬件。
P99 是 1.623ms,是 P50 的 1.8 倍。这个比例还算健康。如果 P99 是 P50 的 5 倍以上,说明存在严重的尾部延迟,通常和显存碎片、动态 shape 重分配、或者偶发的 cudaMalloc 有关。
4.3 用 CSV 做进一步分析
加上 --verbose-csv 可以输出每个请求的详细延迟:
perf_analyzer -m yolov8s \ --concurrency-range 8 \ --latency-report-file latency_report.csv \ --verbose-csv拿到这个 CSV 之后,你可以画延迟分布直方图,看看长尾到底长什么样。如果 P50 是 3ms 但 P99 是 25ms,那 25ms 那些请求就是你要重点排查的对象。
4.4 稳定性测试
上线前除了看峰值性能,还要看长时间运行的稳定性:
perf_analyzer -m bert_classifier \ --request-rate-range 100 \ --measurement-interval 86400000 \ --periodic-concurrency-range 1 \ -f stability_24h.csv这条命令以固定 100 QPS 跑 24 小时,观察吞吐和延迟是否随时间漂移。如果跑着跑着延迟慢慢上升,可能是显存泄漏或者连接池耗尽。
5. 本篇常见错误排查
5.1 连接被拒绝
报错信息通常是Failed to connect to localhost:8001。先确认 Triton 服务在跑,端口没被防火墙挡。如果你用的是 Docker,检查端口映射是否正确。
5.2 模型找不到
Requested model 'xxx' is not found说明模型名写错了,或者模型还没加载完成。用curl localhost:8000/v2/models列出所有已加载的模型。
5.3 输入形状不匹配
Shape mismatch是最常见的错误之一。你需要确认 --shape 里的 tensor 名称和模型配置文件里的名称完全一致,形状也要匹配。如果模型支持动态 shape,压测时用的形状要在允许范围内。
5.4 压测过程中出现 429 错误
Triton 本身不会返回 429,但如果你前面有网关或者限流层,可能会触发。这时候要么降低并发,要么调整限流配置。另外检查一下 Triton 的--max-queue-delay和--max-queue-size参数,队列满了也会导致请求被拒。
5.5 吞吐不随并发增长
如果并发从 4 加到 8 加到 16,吞吐几乎不变,说明 GPU 已经饱和了。这时候增加并发只会让延迟上升。解决办法是增加模型实例数(在 config.pbtxt 里设置 instance_group),或者用多 GPU 部署。
5.6 P99 延迟突然飙升
如果 P99 比 P50 大很多,先看是不是动态 shape 导致的 buffer 重分配。可以在 config.pbtxt 里配置 shape bucket 预分配。另外检查是否有其他进程在抢 GPU 资源。
5.7 LLM 压测的 TTFT 偏高
LLM 的 TTFT 受 prompt 长度影响很大。如果你的 prompts.json 里混了长短不一的输入,TTFT 的分布会很宽。建议按 prompt 长度分组压测,分别看不同长度下的 TTFT。
6. 把压测接入 CI 与后续动作
6.1 自动化性能回归
性能基线建立之后,最怕的是某次提交把性能搞崩了。可以在 CI 里加一个简单的断言脚本:
#!/bin/bash THROUGHPUT_THRESHOLD=8000 P99_LATENCY_THRESHOLD=3000 MODEL_NAME="yolov8s" perf_analyzer -m $MODEL_NAME \ --concurrency-range 8 \ --input-data random \ -f ci_result.csv THROUGHPUT=$(awk -F',' 'NR==2 {print $3}' ci_result.csv) P99=$(awk -F',' 'NR==2 {print $7}' ci_result.csv) if (( $(echo "$THROUGHPUT < $THROUGHPUT_THRESHOLD" | bc -l) )); then echo "FAIL: Throughput $THROUGHPUT < $THROUGHPUT_THRESHOLD" exit 1 fi if (( $(echo "$P99 > $P99_LATENCY_THRESHOLD" | bc -l) )); then echo "FAIL: P99 Latency $P99 > $P99_LATENCY_THRESHOLD" exit 1 fi echo "PASS: All performance metrics within thresholds"把这个脚本挂到 CI 流水线里,每次提交都跑一遍,性能回退就能第一时间发现。
6.2 压测之后的下一步
拿到基线数据之后,你可以做几件事:一是根据 queue 和 compute 的占比决定优化方向;二是用不同的 batch size 和精度组合找到最优配置;三是把压测结果和线上监控数据对照,看看本地基线和生产表现差多少。
如果你在压测之外还需要做模型对话验证或者管理 API Key,可以走前面提到的 TaoToken 入口。压测本身是本地行为,但如果你要对比线上服务的表现,可以先把本地基线跑出来,再用同样的输入去线上做对照。
模型对话入口:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model_chat
Coding Plan 入口:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding_plan
API Keys 管理:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api_keys
接入文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc
6.3 一个实用的压测习惯
每次改完模型配置或者升级 Triton 版本之后,先跑一遍固定并发的压测,和上一次的 CSV 对比。如果吞吐掉了超过 5% 或者 P99 涨了超过 10%,就要查原因。这个习惯能帮你避免很多“上线后才发现变慢”的问题。
压测不是一次性的任务,而是持续的过程。把 perf_analyzer 的命令固化成脚本,把结果存成带时间戳的 CSV,时间长了你会发现性能变化的规律,也能在出问题时快速定位是哪次变更引入的。