单条合成还算快、一开并发就整体变慢,通常不是模型忽然算不动,而是四段耗时中只有一段真的随并发放大:请求排队、推理计算、结果写盘、音频拼接。建议先给每次合成打上时间戳,把这四段分开,再用并发梯度跑同一批文本,看哪一段最先抬起来。抬起来的是推理,问题在排队和实例容量;抬起来的是写盘或拼接,问题在客户端这一侧。
判断路径:先用四个计时点把一次合成拆成请求等待、推理、写盘、拼接,再以并发 1→2→4→8 的梯度记录平均与最大耗时,找出耗时明显上扬的拐点。若只有写盘和拼接随并发增长,先改本地 IO 与拼接方式;若请求等待和重试同步增长,先看服务端排队与限流。拐点以下设并发上限,并用同一批文本回归验证。所有结论以自己环境实测为准。
给每次请求打上开始与结束时间戳
计时点建议放在四个位置:t0 客户端发起请求前、t1 收到首个响应字节、t2 响应体读取结束、t3 音频文件写完并 close 完成。t2 减 t0 是推理加网络的服务端侧耗时,t3 减 t2 才是写盘拖尾。日志用一行 JSON,方便后面按并发数聚合:
{"req_id":"r-0001","text_len":42,"concurrency":4,
"t0":1710000000.100,
"t1":null,"t2":1710000000.842,"t3":1710000000.905,
"wait_ms":742,"write_ms":63,"status":200,"attempt":1}
其中 wait_ms 建议记 t2-t0(服务端可见耗时),write_ms 记 t3-t2。stream 场景下 t1 有意义,一次返回整包时 t1 可留空,不要为了凑字段伪造数值。先只拿一段十几字的短文本串行跑 3 次,确认四个时间戳都有值、wait_ms 与 write_ms 量级合理、t3 不早于 t2,再进入并发测试。计时用单调时钟(如 Python 的 time.perf_counter 或 time.monotonic),避免系统时间跳变。
把并发从 1 逐步提到 N 观察耗时曲线
并发梯度取 1、2、4、8、16,若已知实例规模更小,就从 1 取到自己的上限再加一档。每档跑同一批文本,建议 20~50 条,短句和中长句都放几条,避免只用超短文本得出偏乐观的曲线。每档记录平均耗时、最大耗时和失败数,最大耗时比平均更能暴露排队。
| 并发数 | 条数 | 平均 wait_ms | 最大 wait_ms | 平均 write_ms | 失败数 | 备注 |
|---|---|---|---|---|---|---|
| 1 | 30 | 填实测值 | 基线 | |||
| 2 | 30 | |||||
| 4 | 30 | |||||
| 8 | 30 | 观察拐点 |
判断方式:平均 wait_ms 基本持平、只有 write_ms 上升,属客户端 IO;平均 wait_ms 随并发近似成倍增长、最大耗时远高于平均,多半是排队。不要一次把并发从 1 跳到 16,那样只能看到“慢了”,看不到从哪一档开始慢。
把写盘与拼接环节单独计时
写盘计时要从“拿到完整音频字节”开始,到文件 flush 并 close 结束,与请求耗时分开算:
import time
t = time.perf_counter()
with open(path, "wb") as f:
f.write(audio_bytes)
f.flush()
write_ms = (time.perf_counter() - t) * 1000
如果一次请求要合成长文本,会被拆成多段再拼接,拼接步骤(拼接字节、重采样、转码、改头信息)必须单独计时,不要混进 write_ms。很多“并发变慢”其实是多线程同时写同一目录、同时做一次完整拷贝导致的。对照方法:同一批文本分别写到三个目录——容器挂载卷、本机数据盘、内存盘(仅作对照,不作为最终结论),比较 write_ms 与拼接耗时。若挂载卷明显更慢,先查目录所在磁盘和挂载参数,而不是改模型参数。
确认服务端的排队与限流表现
“算得慢”和“被限流后反复重试”在总耗时上很像,要分开。日志里至少记:HTTP 状态码、是否 429 或 503、Retry-After 头、请求 ID、单次 attempt 序号、该次尝试耗时。同一请求被重试 3 次时,总耗时会被放大,但每次尝试的耗时可能并不高,这种情况调并发没有意义,先降并发或加退避。
retry:
max_attempts: 3
backoff: exponential
base_delay_ms: 200
max_delay_ms: 2000
retry_on: [429, 500, 502, 503, 504]
jitter: true
验证方式:把失败请求按 attempt 分组,对比 attempt=1 与 attempt≥2 的 wait_ms。若重试占比高,先记录限流阈值相关的响应头,再决定是降低并发还是申请更高的服务端配额;具体配额以服务端配置或控制台页面为准。
按测出的拐点设定并发上限
把测试结论落到配置项上,而不是凭感觉写一个数字。若你的调用是本地服务或自建客户端,并发通常落在 worker 池或并发信号量上,例如配置里的 concurrency、max_workers、max_inflight 一类字段;先在配置文件或启动参数里找到它,确认当前生效值,再改成拐点前一档。
synth:
base_url: http://127.0.0.1:PORT
concurrency: 4 # 取拐点前一档
request_timeout_s: 30
output_dir: /data/audio
上限值旁边记上对应数据:并发 4 时平均 wait_ms 与最大 wait_ms 各是多少,并发 8 时是多少,超时和失败数是多少。调低或调高后,用同一批文本重跑一遍,比较平均耗时、最大耗时和失败数是否优于调整前,再决定是否保留。若曲线在并发 4 之后明显变陡,就停在 4 附近;若一直平缓,也不要一次跳到远高于拐点的值,留一档余量便于观察。