纯decode t/s = (生成token数-1) / (总耗时 - TTFT) ← 稳态“打字速度”,主对比指标 ITL(ms) = 1000 / 纯decode t/s ← 每输出一个 token 的耗时 TTFT(ms) = 从请求发出到首个 token。P50/P95 一起看。 总吞吐 tok/s = 生成token / 总耗时(历史旧口径,含 TTFT,只作兼容参考) 怎么读 MTP: - MTP 投机解码会【抬升 TTFT】(首token前要多跑 draft+verify),是机制使然,不是异常。 - MTP 会【降低 ITL / 提升纯decode t/s】——但要在“短提示 + 长输出”才显现。 - 短输出(如 256 token)时 TTFT 占满分母,MTP 收益未摊薄,用它判 MTP 会误判。 - 评 MTP 请设 max_tokens ≥512 再看纯decode t/s;并配合「MTP 接受率」一起看。
—
—
选择一个容器查看日志
| # | 步骤 | MTP | 上下文 | 输出 | 状态 | 纯decode t/s | ITL(ms) | TTFT(ms) | TTFT P95(ms) | decoded | 总耗时(ms) |
|---|
- 长输出(512)、短上文:MTP开 64.61 ≈ MTP关 64.49,几乎无增益——此栈(2×V100 TP2 + AWQ)上 MTP 对稳态解码收益不明显。 - 短输出:数值波动大,TTFT 占满分母,不能作为 MTP 成效依据(“短任务看 MTP 会误判”的实证)。 - 长上下文(Claude Code / agentic)关键发现:MTP【关】在 32k/64k/96k 短输出下解码正常(29.6/35.2/41.4 tps);MTP【开】在 32k 长上文解码塌缩到 ~0.7 tps(引擎日志实锤,慢~55倍,单请求卡死10min)。→ 此栈上 MTP 开 + 长上下文 = 严重负优化,必须关闭。 - TTFT 随 ctx 近线性增长(≈44.8s@32k / 91.8s@64k / 146.2s@96k),每 k-token prefill 约 1.4-1.5s——长上文首 token 延迟由 prefill 主导,MTP 在此是纯开销零收益(只加速 decode 不加速 prefill)。 - 溯源:此前“53 vs 66”=“总吞吐(含TTFT) vs 纯decode”的口径差,不是 MTP 增益。 - 建议:短上文可保持 MTP 开(无增益但无碍);长上文/长上下文服务务必 MTP 关闭。
—(宁缺勿猜)。NCCL_P2P_DISABLE=1,所以 P2P 状态在能力矩阵里如实标为「未知」。加载中…
① 起一个模型 — 到 点一张卡片,在弹窗里选上下文窗口与并发,确认后等载入完成(顶部会显示进度横幅,载入期间各页锁定)。
② 看它跑成什么样 — 看 GPU 显存/利用率、实时吞吐与累计用量; 可以先做一次单次测速(几秒到几十秒)。
③ 要能被引用的数据就跑基准 —
勾选用例 → 设 repeats → 开始,完成后出 HTML 报告(含口径说明与断言明细)。
最短路径命令(不用点界面):
.venv/bin/python run_cli.py --skip-heavy --repeats 5(先要有一个模型在 8080 上跑着)。
| 页面 | 干什么 | 什么时候来 |
|---|---|---|
| ① 总览 | 当前模型 / Embedding、GPU 显存与利用率、实时吞吐与排队、累计用量与四层成本 | 先确认"现在在跑什么、卡什么状态" |
| ② 模型列表 | 点卡片切换模型(可选上下文与并发);2.2 宿主硬件;2.3 切换过程日志 | 换模型、查启动参数 |
| ③ 运行比较 | 对当前模型做单次测速并归档到 SQLite;3.2 历史表、3.3 最佳前 20 配置 | 快速看一眼当前模型表现、和过去比 |
| ④ 基准测试 | 跑 case_01~11 套件(性能 + 质量),出 JSON 与 HTML 报告 | 要一份可引用、带口径的数据 |
| ⑤ MTP 对比 | 一键串跑「切模型(MTP 开/关) → 长上下文测速 → 汇总」;6.3 是历史结论快照(不随数据更新) | 评估投机解码值不值得开 |
| ⑥ 容器日志 | 选容器看日志,可 2s 自动刷新、下载 .log | 排障 |
| ⑦ 使用说明 | 本页 | — |
| ⑧ 硬件与能力 | 本机实测硬件;GPU 目录(含本机没有的卡);能力矩阵(含本机做不到的项与开启路径);互联拓扑原文 | 判断"要不要换卡、换什么" |
| ⑨ 选型建议 | 跨模型 × 跨硬件矩阵;质量层;约束式推荐;导入别的机器的结果 | 选型、给客户出结论 |
| ⚙ 设置(右上角齿轮) | 主题(浅色 / 夜间 / 跟随系统)、写令牌、关于 | 改外观、配令牌 |
| 指标 | 定义 | 怎么用 |
|---|---|---|
| 纯 decode t/s | (生成 token − 1) ÷ (总耗时 − TTFT) | 主对比指标:稳态"打字速度",不含首 token |
| ITL (ms) | 1000 ÷ 纯 decode t/s | 每输出一个 token 的耗时。GenAI-Perf 口径,不含 TTFT——LLMPerf 口径含 TTFT,同一份数据能差 70%+,所以报告/CLI/前端都写明口径 |
| TTFT (ms) | 发出请求 → 收到第一个 token | 交互首秀体验。总览 1.4 那个是本会话累计的聚合均值;要分档/分位看 ③④ |
| prefill tok/s | prompt token ÷ prefill 耗时 | 长提示处理能力;长上下文/编程 agent 场景看它 |
| E2E (ms) | 整个请求耗时 | 兜底对比 |
| 总吞吐 tok/s | 生成 token ÷ 总耗时(含 TTFT) | 旧口径,只作兼容参考——别用它判 MTP(会把 TTFT 的损失藏起来) |
| goodput | 同时满足 TTFT / ITL / E2E 三条 SLO 的请求 ÷ 全部请求 | 唯一回答"有多少请求既对又快"。分母含失败请求(剔掉会让达标率虚高) |
| 稳定性 (CV) | 同一工况重复测量的变异系数 | ≤8% 高 / ≤18% 中 / 其余低;样本 <3 显示"样本少"(不是"抖") |
| P95 | 最近秩分位数 | n<20 一律不给:n<20 时"P95"恒等于最大值(数学下界,不是经验值) |
三条读数的纪律(不知道这三条,报告里的数字很容易被读反):
① "未评估" ≠ 0 分:没有断言的用例(纯时延/压力档)显示"未评估",不进通过率分母——它是"没测",不是"测得差"。
② 缺失 ≠ 0:读不到的数字显示 — / N/A,绝不填 0(0 TFLOPS 是"没有算力",— 是"没核实",含义完全相反)。
③ 分档用例看指定档位:case_05/06/08/09 的用例级中位数是最慢档、case_07(多轮)是末轮——跨档混算出的那个数不对应任何真实工况,报告里降级成脚注。
用例分两层:性能(快不快)与质量(对不对)。两者分开陈述,不合成单一"综合得分"。
| 用例 | 维度 | 难度 | 说明 |
|---|---|---|---|
| case_01 纯文本生成基准 | 性能 | 简单 | 基础吞吐/延迟基线 |
| case_02 简单工具调用延迟 | 性能 | 简单 | 单次 function call |
| case_03 文件读取+分析 | 性能 | 中等 | 结构化输入解析 |
| case_04 并行工具调用 | 性能 | 中等 | 一次多工具 |
| case_05 长上下文处理 | 性能 | 耗时 | 500/1K/2K/4K 分档 |
| case_06 复杂推理任务 | 性能 | 耗时 | 推理难度分档 |
| case_07 多轮对话累积 | 性能 | 耗时 | 序列模式,看末轮增长 |
| case_08 并发压力测试 | 性能 | 压力 | 扫档 1/2/4/8/16,逐档达标率连起来看拐点 |
| case_09 长上下文注入 | 性能 | 耗时 | Prefill 档位可在 4.1 里用芯片编辑器改 |
| case_10 长上下文检索命中率 | 质量 | 中等 | 在上下文里植入随机口令,统计找回比例 + 逐深度明细(lost in the middle) |
| case_11 指令遵循 / 结构化输出 | 质量 | 简单 | 8 条可机械判定的约束(JSON 结构/类型/数组长度/禁用词/长度上限),统计逐条满足率 |
repeats(重复次数):界面 1–50。两个门槛不要混:N≥5 才能算中位数/稳定性(协议底线),N≥20 才给 P95(数学下界)。想要可引用的尾延迟就把 repeats 提到 20。
「跳过耗时用例」:只跳过难度标"耗时"的(case_05/06/07/09)。质量用例不会被跳过——一个只测速度、从不测能力的冒烟跑不能用来选型。
口径版本:性能口径 config.MEASURE_VERSION、质量口径 config.QUALITY_VERSION,
不同版本的数字不可比(跨运行对比图会自动隔离,旧数据一律保留、不迁移)。当前值在 ⑨ 选型建议与设置里都能看到,别抄文档里的版本号——它会变。
报告:/report.html 是最近一次;历史每一轮也能单独出报告(④ 的 4.2 列表里点开)。
| 症状 | 多半是什么 | 怎么办 |
|---|---|---|
| 某个卡片一直"加载中…" | 该页的接口没返回或返回了异常 | F12 → Network 看那个 /api/... 的状态码;命令行 curl -s 127.0.0.1:9100/api/status 对照 |
| 左下"数据更新于"不再变化 | /api/status 拉不到(后端挂了或 nvidia-smi 卡住) | 看控制台日志(/tmp/modelrun-run.log 或 ./run.sh 的输出),必要时 ./run.sh 重启(它会先停掉自己起的旧实例) |
| 点模型卡片没反应 / 提示 409 | 已有模型正在载入(防并发切换),或 MTP 序列在跑 | 等进度横幅走完;或先停 MTP 序列 |
| 切换失败、载入超时 | 显存不足、容器卡在 created/restarting、前台握手超时 | 看 2.3 或 ⑥ 的容器日志;vLLM 大上下文 + CUDA 图捕获本身可能要几分钟 |
| 切走了但显存没释放 | 容器没真正退出(restart 策略把它拉起来了) | 停止规则是"先 update --restart no 再 stop";用 ⑥ 或 docker ps -a 核对残留容器 |
| 写操作返回 401 | 后端开了写鉴权,但浏览器里没存令牌 | 右上角 ⚙ → 「写操作令牌」填 MODELRUN_WRITE_TOKEN 的值 → 保存(状态会变绿) |
| 页面提示"上次运行被中断" | 上次基准没跑完(进程重启/被停) | 知情后点"知道了";结果文件里那条会如实标 interrupted,不会假装还在跑 |
| 测速按钮报"无运行中的大模型" | 8080 上没有模型 | 去 ② 起一个;或确认容器发布的是 8080 |
指标显示 N/A(n=5<20) | 样本量不到 P95 的数学下界 | 把 repeats 提到 ≥20 重跑——这是提示补救方式,不是"数据坏了" |
| 显示"未评估" | 该用例没有断言(纯时延档) | 正常,不是失败;它不进通过率分母 |
| 报告的配色和这里不一样 | /report.html 是独立生成的静态页 | 预期行为:它不跟随控制台主题 |
| ⑨ 矩阵只有一列 / 写着"型号未记录" | 那些 run 产出于"硬件档案"功能之前 | 跑一轮新的基准测试即可带上本机硬件;别的机器用 9.4 导入 |
| ⑨ 推荐说"不给出推荐" | 没有候选满足约束(最常见:样本量 < 最少样本数) | 看它列出的未上榜原因,放宽约束或补跑缺失的评测项 |
curl -s 127.0.0.1:9100/api/status # 运行态 + GPU + 实时吞吐 + 用量/功耗 curl -s 127.0.0.1:9100/api/models # 模型清单(含 running 标记) curl -s 127.0.0.1:9100/api/hardware # 硬件档案:本机实测 + GPU 目录 + 能力矩阵 curl -s 127.0.0.1:9100/api/matrix # 跨模型 × 跨硬件矩阵(只看已实测组合) curl -s 127.0.0.1:9100/api/quality # 质量层各维度得分与状态 curl -s 127.0.0.1:9100/api/bench/results/list # 历史基准结果列表 curl -s 127.0.0.1:9100/api/audit # 写操作审计(谁在何时切了什么) curl -s '127.0.0.1:9100/api/selection?min_samples=5&min_quality_pct=70' # 约束式推荐 curl -s 127.0.0.1:9100/api/auth/status # 写鉴权是否开启 + 受保护的端点清单
T=$MODELRUN_WRITE_TOKEN
# 切换模型(ctx / parallel 给 llama.cpp;max_num_seqs / mtp 给 vLLM)
curl -s -X POST 127.0.0.1:9100/api/switch -H "X-ModelRun-Token: $T" \
-H 'Content-Type: application/json' \
-d '{"model":"<模型 id>","ctx":131072,"max_num_seqs":8,"mtp":false}'
# 跑基准(后台任务,用 /api/bench/status 轮询进度)
curl -s -X POST 127.0.0.1:9100/api/bench/run -H "X-ModelRun-Token: $T" \
-H 'Content-Type: application/json' \
-d '{"cases":["case_01_generation","case_10_needle"],"repeats":20}'
curl -s -X POST 127.0.0.1:9100/api/bench/stop -H "X-ModelRun-Token: $T"
curl -s -X POST 127.0.0.1:9100/api/stop -H "X-ModelRun-Token: $T" \
-H 'Content-Type: application/json' -d '{"target":"llm"}' # llm | embedding | all
# 导入别的机器产出的 run_*.json(payload 是文件内容)
curl -s -X POST 127.0.0.1:9100/api/matrix/import -H "X-ModelRun-Token: $T" \
-H 'Content-Type: application/json' \
-d "{\"payload\": $(cat run_xxx.json), \"hardware\": {\"gpu_model\":\"A100-SXM4-80GB\",\"gpu_count\":2}}"
curl -s 127.0.0.1:8080/v1/models # 正在服务的大模型(--alias) curl -s 127.0.0.1:8080/health # 模型健康 curl -s 127.0.0.1:8081/v1/models # Embedding(常驻 8081) cd llmconsole_app .venv/bin/python run_cli.py --skip-heavy --repeats 5 # 快速冒烟 .venv/bin/python run_cli.py --all --repeats 20 # 含耗时用例,够 P95 .venv/bin/python run_cli.py --cases case_10_needle,case_11_instruction # 只跑质量用例 .venv/bin/python run_cli.py --strict --min-goodput 90 # 当 CI 门槛(退出码判定)
外部访问:内网 https://192.168.1.190/ 与 http://127.0.0.1:9100/;
公网域名经 nginx 反代到本机(/ → 控制台,/v1/ → 8080,/emb/ → 8081)。
| 路径 | 内容 | 保留策略 |
|---|---|---|
data/results/run_*.json | 每轮基准的完整结果(含用例明细) | 只保留最近 50 个(config.MAX_RESULTS) |
data/results/latest.json | 最近一轮(⑨ 质量层与报告读它) | 覆盖写 |
data/results/job_state.json | 基准任务的进度状态 | 覆盖写;进程重启后未跑完的标 interrupted |
data/results/imported/ | 从别的机器导入的结果 | 只追加不覆盖(同名自动加后缀) |
data/benchmarks.db | 单次测速的 SQLite 归档(③ 的历史) | 不删;对比分组按实际采样参数隔离 |
data/audit/audit.jsonl | 写操作审计(含被拒的尝试) | 只追加;超 5MB 滚动改名,不删历史 |
report/report.html | 最近一轮的可读报告 | 覆盖写 |
复现锚点:报告的"环境信息"里记了镜像 digest(tag 会被重建覆盖,digest 才是唯一身份)、 完整启动参数、驱动/CUDA 版本、以及本机硬件档案。两次数字不同时,先比 digest 判断是不是同一份镜像跑出来的。
口径隔离:换口径靠版本号隔离,不靠删数据。跨版本的数字不会并排放进同一张图/表, 旧运行会在报告里以"旧口径运行未纳入对比(N 次)"出现。
安全边界:只读接口(状态、报告、矩阵、推荐)默认开放;破坏性写操作
(切换 / 停止 / 跑基准 / 跑序列 / 导入结果)需要写令牌。令牌只存在本机浏览器、
随请求头发送、不进 URL。未设置 MODELRUN_WRITE_TOKEN 时鉴权关闭,
此时导航栏与设置里都会显示警告,审计日志把 actor 记成 anonymous(auth-disabled)——
"没人管"必须看得见。仍未保护的是测速接口(不改服务配置,但会占算力)。
本仓库没有 git:改动历史记在 01-docs/1cat-vllm-worklog.md,
口径/数字相关的问题请一并记在那里(这是唯一的改动证据链)。
| 术语 | 含义 |
|---|---|
| TTFT | Time To First Token,首 token 延迟。交互体感的主要来源,由 prefill 主导 |
| ITL | Inter-Token Latency,相邻输出 token 的间隔。本项目用 GenAI-Perf 口径(不含 TTFT) |
| prefill / decode | 推理的两个阶段:先把提示算一遍(compute-bound),再逐 token 生成(memory-bound) |
| goodput | SLO 达标率:同时满足 TTFT/ITL/E2E 三条约束的请求占比。分母是全部请求 |
| SLO | 服务目标阈值。默认 TTFT ≤2000ms / ITL ≤60ms / E2E ≤30000ms,可用环境变量或 CLI 覆盖 |
| MoE | Mixture of Experts,总参大但每 token 只激活一小部分 → prefill 快、质量接近大模型 |
| TP | Tensor Parallel,把一个模型切到多张卡上并行。效率取决于卡间互联(NVLink / PCIe) |
| MTP / 投机解码 | 先猜几个 token 再验证。降 ITL、抬 TTFT;短输出下看不出收益,长上下文下本机实测曾严重劣化 |
| KV cache | 注意力键值缓存。长上下文的显存大头,量化它(如 fp8_e5m2)能撑更大的 ctx |
| 前缀缓存 | 命中相同前缀时跳过重复 prefill。命中与未命中的输入成本差一个量级(见 ① 的四层成本) |
| 量化 | AWQ / GPTQ / NVFP4 / GGUF Q4_K_M 等。解码受显存带宽限 → 减权重字节直接提速,代价是精度 |
| NVFP4 | 4bit 浮点权重量化格式。V100 无原生支持,靠 1Cat-vLLM 的特调内核模拟 |
| digest | 镜像内容哈希。tag 会变、digest 不会——判断"是不是同一份镜像"只能看它 |
| Pareto 前沿 | 在多目标(质量/延迟/成本)里"不被任何其它配置全面压过"的那些配置 |
localStorage.mr_theme),不影响别人;
主题在首屏绘制前应用,不会闪一下浅色。X-ModelRun-Token 发出——不写进 URL(URL 会进 nginx 日志与浏览器历史)。
后端未设置 MODELRUN_WRITE_TOKEN 时鉴权是关闭的,此时上方会显示警告。/report.html 是独立生成的静态页面,
不跟随这里的主题设置(它有自己的配色)。它的口径说明与这里的口径版本是同一个来源,
两边数字应当一致;不一致就是有 bug,请记录到 worklog。