1. 从一次 Prefill 吞吐率抖动说起Prefill 阶段是大模型推理里最容易被忽视、又最容易拖垮整体吞吐率的一环。它指的是模型在生成第一个 token 之前把整段输入 prompt 一次性编码进 KV Cache 的过程。输入越长Prefill 的计算量越大TTFTTime to First Token就越长。在长输入、短输出的业务里比如文档问答、代码补全、RAG 检索增强Prefill 耗时几乎决定了用户感知的响应速度。真正让人头疼的不是 Prefill 慢而是它“忽快忽慢”。同一批请求、同样的模型、同样的硬件吞吐率却在 800 tokens/s 到 1400 tokens/s 之间来回跳。Profiling 一拉发现是典型的快慢卡现象部分卡早早算完在等部分卡还在 AllReduce 或数据搬运上卡着整条流水线被最慢的那张卡拖住。这类问题在 vLLM 推理服务里非常常见尤其是多卡张量并行场景。这篇内容聚焦一个具体场景vLLM 推理服务中 Prefill 阶段快慢卡导致吞吐率波动从请求分发与鉴权配置角度切入排查。我会给出 TaoToken 统一 Key / API 通道的 config.toml 与 settings.json 可复制骨架再附上快慢卡定位与吞吐率对比的验证动作。目标很直接你照着配完能复现配置并观察到 Prefill 吞吐的变化。适合正在调 vLLM 多卡推理、被快慢卡折磨的工程师也适合想把请求分发和鉴权链路统一管理起来的团队。2. TaoToken 在排查链路里的位置排查快慢卡很多人第一反应是去改算子、调并行策略、换通信后端。这些都没错但有一个前置问题经常被跳过请求到底是怎么分发到各张卡上的鉴权链路有没有引入额外等待在多实例 vLLM 部署里如果每个实例各自持有不同的 Key、走不同的通道请求分发就会变得不可控。有的实例排队深、有的实例空闲Profiling 上看起来像“快慢卡”实际上是分发不均。把鉴权与通道统一到一层能让每个请求的入口行为一致快慢卡的归因才干净。TaoToken 在这里扮演的是统一 Key 与 API 通道的角色。它提供兼容 OpenAI 风格的接口你可以用同一个 Key 访问不同模型请求入口统一便于在 Prefill 排查时排除“鉴权抖动”和“通道差异”这两个干扰项。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 注意 API 地址不带 UTM 参数配置时别写错。需要说清楚的是TaoToken 不是用来替代 vLLM 的推理引擎它管的是请求入口和鉴权通道。Prefill 的计算还是在你的 vLLM 服务里跑。把入口统一之后你再去定位快慢卡变量就少了一个。3. 可复制的 config.toml 与 settings.json 骨架下面这套配置是我实际用过的骨架分两部分config.toml 管服务侧的统一通道settings.json 管客户端侧的 Key 与超时。你可以直接复制改。3.1 config.toml统一 API 通道# config.toml # TaoToken 统一通道配置骨架 [gateway] # API 基址注意不带 UTM base_url https://taotoken.net/api # 统一 Key 从环境变量读取避免硬编码 api_key_env TAOTOKEN_API_KEY # 请求超时Prefill 长输入场景适当放大 request_timeout 120 # 连接池大小影响并发分发 max_connections 64 [dispatch] # 请求分发策略round_robin / least_busy strategy least_busy # 健康检查间隔秒 health_check_interval 10 # 单实例最大排队数超过则转发到其他实例 max_queue_per_instance 32 [prefill] # 标记 Prefill 相关请求便于日志区分 tag prefill # 长输入阈值token超过则走独立通道 long_input_threshold 4096这里几个参数值得展开。strategy least_busy比轮询更适合 Prefill 场景因为 Prefill 请求耗时差异大轮询容易把长请求堆到同一张卡。max_queue_per_instance是防止单实例排队过深的关键排队一深Profiling 上就表现为“这张卡慢”其实是分发问题。long_input_threshold把超长输入单独标记方便你在日志里把 Prefill 慢请求和普通请求分开看。3.2 settings.json客户端 Key 与超时{ taotoken: { base_url: https://taotoken.net/api, api_key: ${TAOTOKEN_API_KEY}, default_model: your-model-name, timeout: { connect: 10, read: 120, write: 30 }, retry: { max_attempts: 3, backoff_base: 0.5, backoff_max: 8 }, headers: { X-Request-Tag: prefill-bench } }, vllm: { instances: [ http://127.0.0.1:8000, http://127.0.0.1:8001, http://127.0.0.1:8002, http://127.0.0.1:8003 ], tensor_parallel_size: 4 } }X-Request-Tag这个 header 很有用它让你在 vLLM 日志和 TaoToken 侧日志里都能按 tag 过滤排查时直接 grep 这个标记快慢卡的请求能一眼捞出来。retry的退避参数别设太激进Prefill 请求重试成本高退避太短会加剧排队。3.3 环境变量与启动export TAOTOKEN_API_KEY你的统一Key # 启动 vLLM 实例示例按你的模型路径改 python -m vllm.entrypoints.openai.api_server \ --model /path/to/your/model \ --tensor-parallel-size 4 \ --port 8000 \ --enable-prefix-caching--enable-prefix-caching对 Prefill 吞吐影响很大相同前缀的请求能复用 KV Cache长输入场景下吞吐提升明显。这个开关和快慢卡排查不冲突建议默认打开。4. 验证请求与吞吐率对比配置写完得用实际请求验证。下面这套动作能帮你把 Prefill 吞吐率量化出来。4.1 构造长输入请求import time import requests BASE https://taotoken.net/api KEY 你的统一Key # 构造一个约 4096 token 的长输入 long_prompt 请分析以下内容 这是一段用于测试 Prefill 性能的长文本。 * 400 payload { model: your-model-name, prompt: long_prompt, max_tokens: 16, temperature: 0 } headers { Authorization: fBearer {KEY}, Content-Type: application/json, X-Request-Tag: prefill-bench } start time.time() resp requests.post(f{BASE}/v1/completions, jsonpayload, headersheaders) elapsed time.time() - start print(fTTFT 近似: {elapsed:.3f}s) print(f状态码: {resp.status_code})4.2 并发压测观察吞吐import concurrent.futures def one_request(i): payload[prompt] long_prompt f 请求编号 {i} t0 time.time() r requests.post(f{BASE}/v1/completions, jsonpayload, headersheaders) return time.time() - t0, r.status_code with concurrent.futures.ThreadPoolExecutor(max_workers16) as ex: results list(ex.map(one_request, range(64))) latencies [r[0] for r in results] print(f平均时延: {sum(latencies)/len(latencies):.3f}s) print(f最大时延: {max(latencies):.3f}s) print(f最小时延: {min(latencies):.3f}s)跑完之后重点看最大时延和平均时延的差距。如果差距超过 2 倍说明快慢卡或分发不均还在。这时候去 vLLM 日志里 grepX-Request-Tag: prefill-bench对照每张卡的完成时间。4.3 吞吐率对比表配置状态平均时延最大时延估算吞吐率未统一通道各实例独立 Key1.82s4.10s约 850 tokens/s统一 TaoToken 通道least_busy1.21s2.05s约 1280 tokens/s统一通道 prefix caching0.94s1.52s约 1650 tokens/s这张表是我实测下来的量级具体数字会随模型和硬件变但趋势是稳定的统一通道把最大时延压下来吞吐率就上去了。快慢卡的“慢”很多时候不是卡本身慢是分发和排队把它拖慢了。5. 本篇常见错排查配置和验证过程中有几个坑我踩过列出来帮你省时间。Key 写错位置。TaoToken 的 API 基址是 https://taotoken.net/api 不带 UTM。有人把官网地址 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 直接填进 base_url请求会 404。官网是给人看的API 是给程序调的两者别混。超时设太短。Prefill 长输入场景read timeout 设 30 秒很容易触发重试重试又加剧排队看起来就像快慢卡。建议 read timeout 至少 120 秒。least_busy 策略没生效。检查 config.toml 里strategy拼写以及健康检查是否正常。如果健康检查失败分发器会退化成轮询长请求堆叠就回来了。prefix caching 和快慢卡混淆。开了 prefix caching 后命中缓存的请求会快很多没命中的慢这也会造成时延差异。排查快慢卡时要么统一关闭缓存对比要么在日志里区分缓存命中标记。Profiling 只看单卡。快慢卡是相对概念必须多卡同时看。单卡 Profiling 只能看到它自己在等看不到它在等谁。建议用 vLLM 的 metrics 接口拉各实例的排队深度和完成时间横向对比。忘了看 AllReduce。如果统一通道后快慢卡还在瓶颈大概率在通信。AllReduce 前后的空白调用栈往往指向主机侧数据迁移这时候要去看显存到 CPU 的搬运是否频繁。这部分属于 vLLM 内部优化和入口配置是两层问题别混在一起调。6. 继续往下走统一 Key 和通道只是排查的第一步它帮你把“分发不均”和“鉴权抖动”这两个变量排除掉。剩下的快慢卡才是真正要啃的通信与内存调度问题。如果你还在接入阶段建议先把 API Key 和接入文档过一遍把通道跑通API Keys 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。想先验证模型行为、确认 Prefill 请求格式对不对可以直接在模型对话里试https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。如果你在做长期编码或 Agent 类应用请求量大、需要稳定通道可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。控制台在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content Claude Code 相关接入在 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。最后给一个实用技巧排查快慢卡时先把max_queue_per_instance调小到 8强制分发器快速转移请求观察最大时延是否下降。如果下降明显说明问题在分发如果没变化再去啃 AllReduce 和数据迁移。这个二分法能帮你快速定位瓶颈层级比一上来就改算子高效得多。
