2026/9/4 12:03:40

九大推理服务商延迟评测:从指标到实战的选型指南

九大推理服务商延迟评测:从指标到实战的选型指南 如果你最近在关注模型推理服务应该会注意到一种新的“卷法”同一个模型版本会被多家服务商同时上架甚至在标题里直接拿延迟数据互相比较。DeepSeek V4 Flash 0731 latency numbers from nine providers这个标题翻译成开发语言就是一句话这里有九个推理服务商对同一个模型的延迟数据到底该怎么选先看这个模型名能读出什么。DeepSeek V4代表主版本Flash通常意味着一个偏向低延迟、低成本、适合高频调用的子版本0731则表示 checkpoint 或部署快照日期。对开发者来说0731这类日期版本标识很重要它防止你在不知情的情况下被服务商悄悄切换到另一个权重。更重要的是模型权重可以一致但部署它的服务商未必一致。同一个模型交给九家 provider 之后首字延迟、生成速度、稳定性、限流策略和成本都可能相差很大。这篇文章不会给出“某家一定比某家好”的单一结论因为抛开业务场景谈延迟排名没有意义。文章会讲清楚四件事延迟评测到底该看哪些指标如何搭建一套能复跑的九家评测脚本如何看待 P50/P99、流式与非流式的差异实际选型时有哪些坑。读完你可以照着自己跑一遍得到自己的结果而不是轻信一份没有注明测试条件的排行表。1. 当同一个模型版本出现在九家 provider 时真正要关注什么这里先给一个明确判断模型能力决定生成质量provider 的推理架构和调度策略决定你看到的延迟。同一个模型名出现在九家 provider 上不代表九家运行的是完全一样的二进制和配置。许多人习惯把模型抽象成一个黑盒认为“同一个模型谁的 API 快就用谁”。但在生产中模型权重只是结果的一部分服务商如何把权重跑起来才是真正的变量。这些变量包括推理引擎不同常见引擎有 vLLM、TensorRT-LLM、SGLang、TGI 等不同引擎在连续批处理、算子优化、KV Cache 管理上的策略差异很大直接表现为请求高峰期的吞吐和时延表现不同。硬件规格不同A100、H100、L40S 甚至国产加速卡显存带宽和算力差异会直接影响 prefill 和 decode 速度。量化与精度策略不同有的服务商可能用 FP8 甚至 INT4 部署换来更高吞吐但极端情况下会影响生成质量。实例调度和排队策略不同有的 provider 为每个请求预留独立显存延迟低但并发成本高有的走高密度批处理路线延迟中位数不高但 P99 可能偏高。API Gateway 和网络链路不同请求到达数据中心的路由、响应压缩、连接复用策略都会反映在端到端延迟上。缓存策略不同如果服务商实现了 prompt 前缀缓存或语义缓存重复或相似请求可能会大幅缩短首字延迟。这也解释了为什么同一个模型会需要“来自九家 provider 的延迟数据”在权重层面的差距已经缩小之后部署层面的差异被放大了。如果你是做 C 端对话产品可能更关心流式首字速度如果你是做离线数据处理管道可能更关心每百万 token 的成本和稳定吞吐。两种业务面对同一份延迟榜单得出来的结论很可能不一样。所以看任何 provider 对比数据之前先反问一句这份数据是在什么硬件、什么并发、什么 prompt 长度、什么输出长度下测出来的如果这份数据不能对应你的业务形态那它只能作为参考不能作为选型依据。2. 延迟评测要先分清楚四类指标延迟这个词在模型推理语境下经常被混用。实际上一次 API 调用里的“耗时”可以拆成多个阶段不同阶段对用户体验的影响完全不同。为了便于后续写脚本先把四类核心指标讲清楚。指标含义主要决定因素适合关注它的场景TTFTTime To First Token从请求发出到收到第一个输出 token 的耗时prefill 阶段性能、排队情况、网络链路对话、客服、实时流式交互端到端延迟从请求发出到完整响应结束的耗时prefill decode 网络 排队非流式调用、批量生成TPOT / ITL平均每个输出 token 的间隔或相邻 token 间延迟decode 阶段速度、批大小、显存带宽长文生成、流式阅读体验吞吐量每秒生成的 token 数服务端整体并发处理能力离线批处理、高并发内部管道这四个指标不是孤立存在的。比如流式对话场景中TTFT 直接决定用户“能不能很快看到回应”但如果 TPOT 很慢用户看到的前几个字很快后面的内容却一个字一个字往外挤体验依然很差。反过来非流式批处理任务中用户根本不看中间过程只有端到端延迟和吞吐量有意义。还有个容易混淆的点端到端延迟低不代表 TTFT 低。有的 provider 对短请求做了快速完成但如果请求本身很短你只能看到总耗时看不到“第一个字等待了多久”。所以评测代码里必须把 TTFT 单独打点不能让端到端延迟掩盖了首字延迟的问题。除了上述指标还应该统计失败率和重试率。一次请求如果因为超时或限流失败即便延迟数据再好看也不能用于生产环境。尤其是高并发场景P99 延迟加上失败率才是一份完整的延迟画像。3. 评测前把数据口径固定下来很多人跑 provider 对比第一次测完发现结果和网上的榜单差很多往往不是 provider 变了而是数据口径不一致。以下七个口径必须在测试前固定。3.1 请求文本一致且可复现评测必须使用一组固定的 prompt不能每天随便想几个问题来测。固定 prompt 的文本、长度和难度才能保证不同 provider 之间的输入条件一致。更严谨的做法是给每个 prompt 一个固定编号并把测试文本提交到仓库方便后续追溯。3.2 采样参数一致temperature、top_p、max_tokens、presence_penalty等参数要固定。不同服务商对参数的默认值可能不同比如没传max_tokens时有的服务商默认很短有的默认很长。参数不一致时延迟差异可能来自服务商配置而不是模型执行性能。3.3 是否使用流式必须分开流式和非流式的耗时结构不同。流式接口从 HTTP 响应头到达开始就能逐步收到 token非流式接口必须等全部 token 生成完才把完整结果一次性返回。评测时不能混在一起否则 TTFT 的定义会出现偏差。3.4 输出长度上限一致max_tokens的取值对端到端延迟影响巨大。生成 64 个 token 和生成 1024 个 token时长完全不是一个量级。如果要观察长文本能力必须把max_tokens固定在一个足够大的值比如 1024如果要模拟真实短问答则固定为 128。3.5 客户端和服务商区域固定延迟数字包含网络传输时间。如果你的客户端和服务商数据中心在同一地域延迟自然很低如果跨地域访问光网络往返就会增加几十甚至上百毫秒。评测时要固定客户端所在区域或者分别记录不同区域的 P50/P99不能拿一个区域的数字和另一个区域的数字直接比较。3.6 测试时段固定服务商在不同时间段的负载不同。白天业务高峰和凌晨空闲时段同一个 provider 的延迟可能相差很大。做九家横向对比时最好在同一时间段轮流或交错测试避免某家正好赶上高峰期而其他家都在空闲。3.7 统计方法统一平均值容易被极端值拉高只看平均值不可靠。更合理的做法是同时记录 P50、P90、P99 和失败率。如果某家平均值低但 P99 很高说明它在突发流量下不稳定如果平均值和 P99 都很低才说明系统整体稳定。4. 环境准备与最小探测代码在写批量评测脚本之前先准备一个最小可运行的探测脚本。下面以 Python 为例通过 OpenAI 兼容接口访问服务商。绝大多数支持模型托管并提供 API 的服务商都兼容 OpenAI 的消息结构即使不兼容也通常会在文档里给出对应 SDK 的调用方式。4.1 环境安装建议使用 Python 3.10 及以上版本并先创建虚拟环境隔离依赖。python -m venv .venv source .venv/bin/activate # 如果你的系统是 Windows激活命令是 # .venv\Scripts\activate pip install openai pyyamlopenai包提供客户端封装pyyaml用于后续读取批量配置。版本以安装时的最新稳定版为准这里不固定版本号。4.2 配置 API KeyAPI Key 是敏感信息不要写死在代码里更不要提交到 Git 仓库。这里使用环境变量保存。export PROVIDER_API_KEY你的服务商API Key export PROVIDER_BASE_URLhttps://api.provider-a.example.com/v1上面的provider-a.example.com只是占位符实际操作时请替换为你所使用服务商文档给出的真实 API Base URL。每个服务商的 API Base URL 不同要以管理后台或官方文档为准。4.3 单次非流式探测下面这段代码发起一次普通的非流式对话请求并测量端到端延迟。# latency_probe.py import os import time from openai import OpenAI client OpenAI( api_keyos.environ.get(PROVIDER_API_KEY), base_urlos.environ.get(PROVIDER_BASE_URL), ) prompt 用三句话解释什么是微积分。 start time.perf_counter() response client.chat.completions.create( modeldeepseek-v4-flash-0731, messages[ {role: user, content: prompt} ], max_tokens256, temperature0.7, streamFalse, ) end_to_end_ms (time.perf_counter() - start) * 1000 content response.choices[0].message.content print(content) print(fend_to_end_ms{end_to_end_ms:.1f})代码里的modeldeepseek-v4-flash-0731是本主题中用到的模型标识。实际请求时请务必查阅服务商提供的“模型 ID 列表”因为不同服务商对同一模型的命名可能不完全一致。如果服务商还没有上架这个版本会返回model_not_found之类的错误。运行命令python latency_probe.py这段代码只是最基础的连通性测试验证了三件事API Key 是否有效、Base URL 是否正确、模型 ID 是否可用。如果这一步都跑不通后面批量评测就无从谈起。5. 九家服务商统一评测配置化与批量流程单次探测通过后就可以把九个 provider 的配置集中到一个文件中写一个统一 runner。这样以后模型版本更新或者服务商列表变化时只需要改配置不需要改代码。5.1 provider 配置文件下面是一个 YAML 配置示例里面只列出了两个 provider实际使用时可以扩展到九家甚至更多。注意文件里的地址是示例占位符不能直接请求。# providers.yaml providers: - name: provider_a base_url: https://api.provider-a.example.com/v1 api_key_env: PROVIDER_A_API_KEY model_id: deepseek-v4-flash-0731 - name: provider_b base_url: https://api.provider-b.example.com/v1 api_key_env: PROVIDER_B_API_KEY model_id: deepseek-v4-flash-0731每个 provider 的api_key_env指向不同的环境变量名这样可以把九家 Key 放在同一台机器的环境变量里互不干扰也不会写入仓库。5.2 批量探测代码下面这段代码会读取providers.yaml对每个 provider 连续发起多次请求并把端到端延迟保存到列表。# benchmark_runner.py import os import time import yaml from openai import OpenAI def load_config(pathproviders.yaml): with open(path, r, encodingutf-8) as f: return yaml.safe_load(f) def create_client(provider): api_key os.environ.get(provider[api_key_env]) if not api_key: raise RuntimeError(f缺少环境变量 {provider[api_key_env]}) return OpenAI( api_keyapi_key, base_urlprovider[base_url], ) def measure_once(client, model_id, prompt, max_tokens256): start time.perf_counter() response client.chat.completions.create( modelmodel_id, messages[{role: user, content: prompt}], max_tokensmax_tokens, temperature0.7, streamFalse, ) latency_ms (time.perf_counter() - start) * 1000 content response.choices[0].message.content return latency_ms, content def run(): cfg load_config() prompt 用一段话解释什么是Redis并给出一个使用场景。 for provider in cfg[providers]: client create_client(provider) latencies [] for i in range(10): latency_ms, _ measure_once(client, provider[model_id], prompt) latencies.append(latency_ms) print(f{provider[name]} sample{i 1} latency_ms{latency_ms:.1f}) avg_ms sum(latencies) / len(latencies) print(f{provider[name]} avg_ms{avg_ms:.1f}) if __name__ __main__: run()这段代码里做了几件关键事情每个 provider 独立创建客户端避免 Key 串号连续请求 10 次避免单次波动的偶然性输出每次延迟和平均值便于快速观察。循环次数可以根据精度要求调整但生产级评测不建议少于 30 次。5.3 增加分位数统计平均值只能给出一个大概感觉。更严谨的评测需要计算 P50、P90、P99。把下面的百分位数函数加入脚本# percentile.py def percentile(data, p): 计算百分位数data 为延迟列表p 为 0~100 之间的数值。 if not data: return 0.0 sorted_data sorted(data) k (len(sorted_data) - 1) * (p / 100) lower int(k) upper min(lower 1, len(sorted_data) - 1) weight k - lower return sorted_data[lower] * (1 - weight) sorted_data[upper] * weight调用方式p50 percentile(latencies, 50) p90 percentile(latencies, 90) p99 percentile(latencies, 99) print(fprovider{provider[name]} p50{p50:.1f} p90{p90:.1f} p99{p99:.1f})如果样本量只有 10 次P99 的统计意义有限建议至少 30 到 50 次再算分位数。样本量越大P99 越能反映真实的长尾情况。6. 流式 TTFT 测试与并发测试对 C 端产品来说流式 TTFT 往往比非流式端到端延迟更接近真实感受。前端页面已经拿到响应状态第一个字越快出现用户就越觉得“系统在思考并回应了”。6.1 流式 TTFT 探测下面代码用流式接口发起请求并记录第一个 content token 出现的时间。# ttft_stream_probe.py import os import time from openai import OpenAI client OpenAI( api_keyos.environ.get(PROVIDER_API_KEY), base_urlos.environ.get(PROVIDER_BASE_URL), ) prompt 请写一篇500字左右的文章介绍分布式系统的基本概念。 start time.perf_counter() stream client.chat.completions.create( modeldeepseek-v4-flash-0731, messages[{role: user, content: prompt}], max_tokens500, temperature0.7, streamTrue, ) first_token_time None received_tokens [] for chunk in stream: if not chunk.choices: continue delta chunk.choices[0].delta if delta and delta.content: if first_token_time is None: first_token_time time.perf_counter() ttft_ms (first_token_time - start) * 1000 print(fttft_ms{ttft_ms:.1f}) received_tokens.append(delta.content) end_to_end_ms (time.perf_counter() - start) * 1000 print(fgenerated_tokens{len(received_tokens)}) print(fend_to_end_ms{end_to_end_ms:.1f})这段代码的关键点在于必须在循环里区分“第一个 content chunk”和“后面的 chunk”。如果只是简单记录请求总耗时你会错过 TTFT 这个对交互体验最重要的指标。6.2 并发压力测试单发单个请求只能反映服务商空闲状态下的表现。真实业务中请求往往并发到来。可以先用一个试探性的并发测试观察服务商在并发下的表现。# concurrent_probe.py import os import time from concurrent.futures import ThreadPoolExecutor from openai import OpenAI BASE_URL os.environ.get(PROVIDER_BASE_URL) API_KEY os.environ.get(PROVIDER_API_KEY) MODEL_ID deepseek-v4-flash-0731 PROMPT 用三句话解释数据库索引的优点。 MAX_TOKENS 128 CONCURRENCY 16 TOTAL_REQUESTS 64 def one_request(_): client OpenAI(api_keyAPI_KEY, base_urlBASE_URL) start time.perf_counter() client.chat.completions.create( modelMODEL_ID, messages[{role: user, content: PROMPT}], max_tokensMAX_TOKENS, temperature0.7, streamFalse, ) return (time.perf_counter() - start) * 1000 with ThreadPoolExecutor(max_workersCONCURRENCY) as executor: latencies list(executor.map(one_request, range(TOTAL_REQUESTS))) latencies.sort() p50 latency_len len(latencies) p50_val latencies[int(p50_len * 0.50)] p90_val latencies[int(p50_len * 0.90)] p99_val latencies[int(p50_len * 0.99)] print(fconcurrency{CONCURRENCY}) print(frequests{TOTAL_REQUESTS}) print(fp50_ms{p50_val:.1f}) print(fp90_ms{p90_val:.1f}) print(fp99_ms{p99_val:.1f}) print(fmax_ms{latencies[-1]:.1f})需要注意这里每个线程都创建新的OpenAI客户端避免共享连接池干扰测量结果。测试时不要一上来就拉 100 并发那样很容易触发服务商的限流导致数据中混入大量 429 错误分位数失真。可以先从 4 并发、8 并发、16 并发逐级增加观察延迟和错误率的变化趋势。6.3 并发测试的正确姿势并发压力测试不是并发越高越好。你的目标是找到“业务真实负载下的表现”而不是把服务商打到限流。如果业务峰值只有 20 QPS那么并发 64 意义不大如果目标是双十一级别的峰值那应该在测试环境逐步增量并记录每个并发档位的 P50/P99 和失败率。评测得到的曲线可以用来反推当延迟增长斜率明显变陡时大概率已经接近服务商的性能拐点。7. 数据怎么看建立自己的选型矩阵九个 provider 的延迟数据跑出来后直接拿延迟排名做决定依然不够。假设你统计完发现 provider A 的 TTFT 短 20%但 provider B 每天有 0.5% 的请求超时provider A 价格是 B 的三倍这种选择就不是一道延迟题而是一道工程权衡题。更推荐的做法是建一张自己的选型矩阵。权重可以按业务调整但至少包含以下维度维度建议权重评估方式响应质量30%用固定业务 prompt 集让人工或自动化指标给结果打分延迟25%用前文脚本得到 P50/P90/P99分场景记录稳定性15%连续多天观察失败率、超时率、P99 波动价格15%按实际 token 用量估算月度成本工程兼容性10%SDK 成熟度、是否兼容流式、是否有 JSON 模式数据合规要求5%是否满足公司内部对数据存储地域和权限的约束权重不是固定的。如果你的业务是一个对响应速度极敏感的实时客服助手延迟和稳定性权重应该更高如果你的业务是跑离线数据清洗那么价格和吞吐量权重会超过单次延迟。先列清楚业务约束再去看数据才不会把“延迟最快”误当成“最适合”。在实际选型时还有一个容易被忽略的原则不要只看最优值要看候选之间是否有显著性差异。如果 provider A 和 B 的 P50 只差 5%而 B 的 P99 比 A 低很多那么 B 可能更适合生产环境。因为 P50 的微小差距在实际体验中很难被用户察觉P99 的稳定表现却能显著降低超时报警数量。8. 容易被忽略的坑九家 provider 的延迟测试看似简单实际跑起来会遇到不少“数字很漂亮但不能复现”的情况。下面几个坑最容易让人误判。8.1 短 prompt 和长 prompt 的结论可能反转如果评测只用了 20 个 token 的短问题一些 prefill 优化强的 provider 会表现非常好。换成长文档总结任务时原来越快的 provider 未必仍然领先因为长 prompt 的 prefill 时间更长不同引擎在长序列下的显存管理策略差异也会拉大。因此评测集必须覆盖短、中、长三种长度。8.2 重复请求触发缓存如果你在循环里连续请求同一个 prompt服务商可能命中了 prompt 前缀缓存或语义缓存导致后面的请求延迟显著下降。缓存本身不是坏事但如果评测目的是了解模型真实推理性能就需要在测试集里混合多个不同 prompt或者记录是否命中缓存。否则你不会知道该服务商在没有缓存时到底有多快。8.3 忽略了响应内容长度差异某些服务商可能会提前截断、或者返回不同长度的内容。延迟对比必须同时检查usage.completion_tokens或实际输出 token 数是否接近。如果 provider A 平均只生成了 80 个 token而 provider B 生成了 120 个 token那么 A 的端到端延迟低很正常并不能说明 A 更快。8.4 只测高峰期或只测低谷期要观察真实的稳定性最好在一天内选择几个不同时段测试早上、下午、晚上各测一轮。这样既能了解平均表现也能看出服务商在高峰时段的延迟是否急剧恶化。8.5 不关注单次超时有时一家 provider 的平均延迟低是因为个别请求超时被监控系统重试服务端实际产生了更长耗时。代码里如果直接把超时请求丢弃结果就会偏乐观。更稳妥的做法是设置明确的超时时间例如 60 秒并记录超时请求数量。超时率超过 0.1% 的服务商即使延迟数据再好看也应该谨慎选择。9. 常见问题与排查方法如果你的批量评测脚本运行不顺利可以按下面的表格排查。问题现象可能原因排查方式解决方案401 Invalid API Key环境变量未设置或设置错误检查终端中是否导入了正确的环境变量重新导出 Key或检查是否有空格404 model not found模型 ID 写错或服务商未上架该版本查看服务商模型列表文档改为服务商文档中的正确模型 ID429 Too Many Requests并发过高触发限流查看返回头的限流字段或日志降低并发增加退避重试请求一直超时客户端与服务商区域网络延迟过高测试不同区域节点检查 DNS 解析选择离业务更近的区域流式响应中 TTFT 很远没有正确解析 SSE 首包确认代码在每个 chunk 上是否判断 content在第一个非空 content chunk 处打点非流式响应很慢但流式很快provider 对非流式接口或流式接口做了不同调度分别测试两类接口根据业务选择更合适的接口类型结果和网上榜单差异大prompt、参数、区域、时段不一致对照评测方法逐项检查统一数据口径后重跑某一 provider 夜间稳定白天波动高峰负载影响白天、夜间各测一轮比较 P99评估时按高峰时段数据为主网络超时问题通常要先区分是“客户端发不出请求”还是“服务端响应慢”。最简单的方式是在代码里记录requests连接建立耗时、首字节耗时和服务端响应耗时。如果某个 provider 的连接建立耗时明显较长问题往往在网络链路而不是推理性能。10. 把评测沉淀成工程机制一次性的评测只能解决眼前的选择问题不能解决模型版本频繁更新时的持续对比问题。更推荐的思路是把延迟评测做成一个可以定期运行的工程任务。10.1 使用版本化测试集将测试 prompt 集合作为固定文件保存例如prompts.jsonl记录每个 prompt 的唯一编号。{id: short_001, prompt: 用一句话解释什么是HTTP。} {id: medium_001, prompt: 请用200字介绍Python的GIL并说明它对多线程的影响。} {id: long_001, prompt: 请完整介绍分布式系统中的CAP理论并给出实际工程中的取舍案例。}每次评测都使用这个文件如果要增删 prompt应通过版本管理提交变更而不是覆盖原文件。这样可以追溯“为什么这次评测结果和上次不一样”背后到底是因为模型版本变了还是测试集变了。10.2 结果入库和监控评测输出不要只打印到终端可以按 JSON Lines 格式落盘。每一天或每一周的评测结果追加到同一个目录最后导入数据库或用脚本做趋势分析。当某个 provider 的 P99 比基准值上升 30% 时可以生产报警提示负责选型的开发团队关注。10.3 锁定模型版本0731这类日期版本标识应该在请求参数里写死。如果服务商后续推出了新版本并把旧版本的模型 ID 映射改为新版本你的评测和生产请求都可能在不知情的情况下切换模型。选型确定后要显式固定模型 ID 和 API 版本号避免“模型悄悄变了”。10.4 至少保留两个备选 provider任何外部服务都可能出现故障。即使选定了主力 provider也应该至少保留一个兼容的备选 provider并定期用同样的测试集做连通性验证。当主力 provider 出现大面积延迟波动时可以通过简单的路由切换把流量切到备选。10.5 定义失败重试策略生产环境接入时要为 LLM 请求设计合理的重试策略。常见做法是第一次 429 或 5xx 时等待 1 秒重试第二次等待 2 秒第三次等待 4 秒最多重试 3 次。重试请求要记录日志方便评估服务商实际可用性。不要把重试逻辑写在调用业务代码里散落各处应该封装成一个统一的 LLM 调用客户端集中处理超时、重试和降级。11. 一点实用建议如果你现在正准备做九个 provider 的延迟对比建议从三个 provider 的小规模测试开始。先把最小探测脚本跑通确认 API Key、Base URL、模型 ID 都没有问题然后用固定测试集跑 30 次非流式和流式请求记录 TTFT、端到端延迟、P50/P99再在有业务语义的 prompt 上做并发测试观察不同并发档位的表现最后把结果放进选型矩阵和成本、稳定性、工程兼容性一起打分。这种做法的核心不是让你得到一份漂亮的报告而是把选型从“看别人标题里的 latency numbers”变成“看自己业务压力下的真实数据”。如果只带走一件事我建议从今天起对 provider 排序时不要只看平均值把 P99 和流式 TTFT 一起打印出来。你的业务如果面向真实用户对话这两个数字比任何榜单里的跑分都更接近你的体感。