前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
返回 AI 情报前线
All News · 全部资讯10183
  • 用 X.509 为多 Agent 建立可验证身份
  • Eon 用统一 MCP 网关简化多云排障
  • 给企业Agent搭建带权限的统一语义层
  • AWS 智能体跨库查询如何落实用户级权限
  • 提示注入检测为何被同义改写绕过
  • Synapse 打通编程助手的记忆与配置
  • vLLM 与 SGLang 新增 TPU 原生后端
  • 给水印检测加门控,减少误选与修复风险
  • 用本地语音组件搭建可打断的 AI 助手
  • Claude Code 误删复盘:命令钩子的盲区
  • Mac 跨应用文本助手如何读取界面
  • 用 AI 编程智能体清理电视广告与预装应用
  • DeepSeek开放生产推理框架DeepInfer
  • 京东大促期间暂停外部安全测试
  • Coral NPU补齐矩阵单元与开源编译链
  • 向量检索与知识图谱如何协同支撑 RAG
  • 让编程助手接入浏览器完成调试闭环
  • 先验证分类器,再让 AI 排查运行轨迹
  • 录制模型响应,隔离评测代码与模型变动
  • 审查 AI 生成 PR,先核对任务边界
  • 用有效吞吐压测确定模型接口并发上限
  • Agent 改一个字段,如何证明操作正确
  • 用多任务决策模型自动分流 CVE 漏洞
  • 逐步核算 Token,定位 Agent 的结构性浪费
  • Agent 生成 SQL,如何控制重跑漂移
  • 审核超时后的任务释放与计费隔离
  • DeepSeek 开源大规模推理框架 DeepInfer
  • DeepInfer 开源,公开大规模推理部署能力
  • Cloudflare预览低价结构化抽取模型
  • Headroom 在本地压缩智能体上下文
  • 智能体改记忆,如何验证没有误伤
  • llama-server 工具控制与停止条件现兼容问题
  • 模型快十倍,智能体为何只快一点
  • 用决策模型做评测,先调分类阈值
  • 编码 Agent 实测:成本之外看交付细节
  • Mercury 3 发布:主打高速生成与百万上下文
  • 用 Node 拆解向量池化与检索质量
  • 补齐 Oracle 方言约束,轻量模型首答率跃升
  • 拆解 Claude Code 的完整智能体循环
  • 邮件 Agent 重试前先校验退信
  • 用信号队列约束 Agent 等待邮件
  • 用 Cloudflare 搭建 Agent 收信中枢
  • 一次字符串替换如何让线上文章翻倍
  • AI 搭项目失败,先确认代码是否拉取成功
  • 检索匹配再高,也救不了过期知识库
  • 本地模型帮你给常用命令起快捷名
  • MCP 无状态改版涉及哪些迁移工作
  • 检查 Supabase 服务端鉴权的信任来源
  • 流式 AI 接口延迟该如何拆分测量
  • 模型故障切换如何追踪实际调用路径
  • 百灵开放五款语音模型供开发者组合
  • 已加载 51 / 10183
8.0
热点
AI SCORE
技术实践2026-10-20 18:15

用有效吞吐压测确定模型接口并发上限

dev.to · AI#性能测试#模型服务#并发控制
Editor brief · 编辑速览

这份 75 分钟实践教程通过逐步提高并发,绘制有效吞吐曲线,寻找容量拐点并制定卸载规则。有效吞吐只统计截止时间内返回且载荷可用的响应,避免总吞吐掩盖超时和无效结果。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

大多数团队为 LLM endpoint 设定并发数时,凭的都是感觉,直到发生事故才发现真正的上限。没有测过的数字就是猜测,而当重试开始放大负载时,猜测的代价会很高。这次工作坊用一套可重复的逐级加压测试取代猜测,绘制 goodput 随施加并发数变化的曲线,让你能够给出有依据的并发上限和请求丢弃规则。下面是一份完整的教学提纲,包括练习、时间安排、可运行的测试程序,以及可以针对自有 gateway 重新执行的示例。

这次工作坊要验证的核心观点

Throughput(吞吐量)和 goodput(有效吞吐量)不是同一个指标,而在免费或共享 endpoint 上,混淆两者很快就会付出代价。Throughput 统计客户端发出的请求所得到的全部响应,包括调用方已经放弃等待后才到达的响应。Goodput 只统计在截止时间内到达、且携带可用内容的响应,这才是产品实际感受到的处理能力。同时跟踪这两个指标的逐级加压测试,通常会看到 goodput 先上升,再趋于平缓,最后下降,而 throughput 仍在继续攀升。这个转折点,就是你要找的拐点。

计时开始前需要准备什么

  • 一个由你控制、位于客户端与模型之间的 gateway,哪怕只是一个 30 行的 HTTP 封装。
  • 一台独立于被测机器的客户端机器,避免客户端的 CPU 争用扭曲 p95。
  • 提前写下截止时间,例如短回答工作负载可以设为 12 秒。
  • 一个 CSV 输出位置,以及写入小文件的权限,因为最终产物是一张数据表,不能只凭感觉。

在这里,免费模型 endpoint 和免费服务器选项确实能为这套方法提供帮助,而不只是营销卖点。把测试程序指向免费模型服务,就能在没有计费账户的情况下演练逐级加压测试;把负载生成器运行在免费服务器上,则可以让笔记本电脑退出关键路径。披露:本文是 MonkeyCode 产品推广活动的一部分。

练习 1——编写测试程序(15 分钟)

下面的测试程序是一份参考实现;我没有针对你的 endpoint 执行过它,因此请把它当作需要亲自运行验证的起点。它会逐级增加受限的并发请求数,为每个结果记录是否满足截止时间,并如实保留失败结果。

# ramp.py — unexecuted reference implementation
import asyncio, csv, json, statistics, time
import httpx

URL = "http://127.0.0.1:8080/v1/chat"   # your gateway, not the vendor URL
DEADLINE_S = 12.0
STEPS = [1, 2, 4, 8, 12, 16, 24, 32]     # offered concurrency, one step at a time
REQS_PER_STEP = 40

async def one(client, sem, idx, out):
    async with sem:
        t0 = time.perf_counter()
        code, ok = None, False
        try:
            r = await client.post(URL,
                json={"prompt": "ping", "max_tokens": 16},
                timeout=DEADLINE_S)
            code = r.status_code
            ok = (r.status_code == 200 and bool(r.text))
        except (httpx.TimeoutException, httpx.TransportError) as e:
            code = type(e).__name__
        out.append({"step": None, "idx": idx, "ok": ok, "code": code,
                    "ms": (time.perf_counter() - t0) * 1000})

async def ramp():
    rows = []
    async with httpx.AsyncClient(http2=False) as client:
        for c in STEPS:
            sem = asyncio.Semaphore(c)
            out = []
            t0 = time.perf_counter()
            await asyncio.gather(*[one(client, sem, i, out) for i in range(REQS_PER_STEP)])
            wall = time.perf_counter() - t0
            for r in out:
                r["step"] = c
            for r in out:
                r["wall_s"] = round(wall, 3)
            rows.extend(out)
            print(c, "concurrency ->", round(wall, 2), "s wall")
            await asyncio.sleep(2)   # let shared queues drain between steps
    return rows

if __name__ == "__main__":
    rows = asyncio.run(ramp())
    with open("ramp.csv", "w", newline="") as f:
        w = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
        w.writeheader(); w.writerows(rows)

练习 2——加入准入控制和截止时间传递(15 分钟)

对没有防护的 gateway 进行逐级加压,测到的是客户端,而不是整个系统。加入有界队列和截止时间请求头,让服务器能够尽早丢弃请求,避免把工作做完时,已经没有人在等待结果。

# gateway_guard.py — sketch, wire into your own framework
import time, asyncio

class Guard:
    def __init__(self, limit=8, max_wait_s=1.5):
        self.sem = asyncio.Semaphore(limit)
        self.max_wait_s = max_wait_s

    async def run(self, deadline_ms, fn, *a, **kw):
        budget = min(deadline_ms / 1000.0, self.max_wait_s)
        try:
            await asyncio.wait_for(self.sem.acquire(), timeout=budget)
        except asyncio.TimeoutError:
            return {"error": "shed", "reason": "queue_wait_exceeded"}
        try:
            return await fn(*a, **kw)
        finally:
            self.sem.release()

每个请求都携带 X-Deadline-Ms,并让 gateway 在调用下游前扣除自身已经消耗的时间。当剩余时间预算低于观测到的最慢调用耗时,就立即丢弃请求,返回 429,并附上 Retry-After 值。在 50 ms 内返回请求丢弃响应,比等待 12 秒后超时,对所有参与方的成本都更低,也能让逐级加压曲线如实反映系统表现。

练习 3——执行逐级加压测试并计算拐点(20 分钟)

分析只需要四行 pandas,公式比工具更重要。每一级的 goodput,等于在截止时间内成功返回的响应数,除以该级测试实际经过的秒数;拐点则是相较前一级,goodput 的提升幅度仍达到事先约定标准的最后一级。

import pandas as pd
df = pd.read_csv("ramp.csv")
df["in_deadline"] = df["ok"] & (df["ms"] <= 12000)
agg = df.groupby("step").agg(
    goodput=("in_deadline", "sum"),
    p95_ms=("ms", lambda s: s.quantile(0.95)),
    wall_s=("wall_s", "first"),
    errors=("ok", lambda s: int((~s).sum())),
)
agg["goodput_rps"] = agg["goodput"] / agg["wall_s"]
agg["gain_pct"] = agg["goodput_rps"].pct_change() * 100
print(agg.round(2))

使用书面规则,不要凭直觉:选择 goodput 增幅至少为 10%,且 p95 仍低于截止时间 60% 的最后一级作为停止点。人们经常跳过第二个条件,而恰恰是这个条件,能防止漂亮的吞吐量数字掩盖糟糕的尾延迟。

示例演练(待填写模板,并非实测数据)

下表特意没有填入真实数值;请将它复制到你的记录卡中,再根据自己的 CSV 填写。在这里展示编造的数字,会违背这次工作坊的初衷。

练习 4——写下丢弃与排队的判断规则(10 分钟)

把曲线转化为一张决策表,让值班工程师无需阅读分析过程,就能直接使用。

局限性,以及哪些人不适合使用这套方法

逐级加压测试测量的是共享 endpoint 当天的运行边界,结果不能直接套用到其他提供商、区域或一天中的其他时段。免费和共享容量可能因客户端无法观测的原因发生变化,因此提供商一侧发生任何变更后,都应重新测试,并把旧曲线当作历史记录。在没有阅读 endpoint 的服务条款或速率限制之前,不要进行激进的逐级加压测试;一旦持续出现 429 响应,就应立即停止。如果你需要合同约定的容量保证、多区域故障切换或租户级隔离,这次工作坊只能提供诊断,不能替代相应的工程建设。

75 分钟安排,以及你最终带走的成果

记录卡就是交付物:截止时间、拐点并发数、拐点处的 p95、请求丢弃规则,以及测试日期。一张标有日期的记录,比无人添加说明的 dashboard 更有价值,因为它能告诉下一位工程师,这个数字是否仍有参考意义。如果你有免费模型服务和免费服务器可用,先在那里完整执行一遍练习,把生产环境预算留给曲线确实证明有必要的改动。

如果要采取进一步行动,可以考虑屏蔽此人和/或举报滥用行为。

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
审查 AI 生成 PR,先核对任务边界
下一篇
Agent 改一个字段,如何证明操作正确