vLLM BF16 vs llama.cpp Q4_K_M · Round 2 · RTX 4090
这一轮的对照是公平的:同一张卡、同一个模型、同一份 1024/256 负载, 两个引擎都扫完了并发 1 到 64 的 7 个点,llama.cpp 侧关掉了 prompt cache。 第一轮只能在 3 个点上比,而且比得不干净。
下面这些缩写不是同义词:每一个量的是流水线上不同的一段,彼此之间有确定的换算关系。
本轮负载:1024 输入 / 256 输出 · 并发 1 → 64 · 两个引擎各扫一遍。
两个引擎的指标都由同一个客户端(vllm bench serve)测量,所以 TTFT(首字延迟)/ ITL / E2EL 这几个数可以直接比。
服务端侧的计数器(KV 占用、抢占、running/waiting)只有 vLLM 有,llama.cpp 只暴露 slot,所以本报告的对照都只用客户端指标。
t=0 客户端发出请求
│
│ [1] 排队 queue_time —— 在 waiting 队列里等调度器放行
│ [2] prefill —— 把 1024 个输入 token 一次算完,写进 KV Cache
│ [3] 前端 + IPC —— 分词、HTTP、API server 与引擎之间的通信
│
━━━━━ [1]+[2]+[3] = TTFT 首字延迟 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
│
│ 第 1 个输出 token 送达
│
│ [4] ITL × 255 —— 相邻两个输出 token 之间的间隔
│ 这 255 个间隔的平均值 = TPOT
│
━━━━━ [1]+[2]+[3]+[4] = E2EL 端到端时延 ━━━━━━━━━━━━━━━━━━━━━━━━━
# 恒等式:E2EL = TTFT + 255 × TPOT
# 吞吐 = 所有请求的输出 token 总数 ÷ 整段测试的墙钟时间(不是 1/TPOT)
| 指标 | 中文 | 量的是什么 | 本轮实测 |
|---|---|---|---|
| TTFT Time To First Token | 首字延迟 | 从客户端发出请求,到收到第 1 个输出 token。它包含排队 + prefill + 前端开销三段,不含后续 decode。 | vLLM 102→787 ms;llama.cpp 93→442 ms |
| ITL Inter-Token Latency | token 间隔 | 相邻两个输出 token 之间的时间。一条请求有(输出数 − 1)个 ITL 样本 —— 样本量大,所以它的 p99 最能暴露抖动。 | vLLM 15.83→22.50;llama.cpp 5.84→35.32 ms |
| ITL p99 | 间隔的尾部 | 把所有 ITL 样本排序后第 99% 那个值。一次卡顿只影响少数间隔,均值和中位数都看不出来,只有 p99 会跳。 | vLLM 16.3→207.6;llama.cpp 6.0→223.8 ms |
| TPOT Time Per Output Token | 每输出 token 耗时 | 一条请求在第 1 个输出 token 之后的平均出字速度,即 (E2EL − TTFT) ÷ (输出 token 数 − 1)。数值上就是这条请求 ITL 的均值。 | vLLM 15.83→43.41;llama.cpp 5.85→57.54 ms |
| E2EL End-to-End Latency | 端到端时延 | 从发出请求到收到最后一个 token 的总时间。恒等式:E2EL = TTFT + (输出数−1) × TPOT。 | vLLM 4,138→11,779;llama.cpp 1,585→15,193 ms |
| 输出吞吐 output token/s | 用户拿到的 | 单位时间产出的输出 token 数。这是用户真正感知的产能。 | vLLM 61.9→1,388;llama.cpp 161.5→1,065 tok/s |
| 总吞吐 total token/s | GPU 处理的 | 输入 + 输出一起算。长输入场景下它上涨、输出吞吐下跌,所以两者不能互相替代。 | vLLM 309→6,939;llama.cpp 807→5,376 tok/s |
三个最容易混的地方
1 · TPOT 和 ITL 均值是同一件事的两种算法。 TPOT 是每条请求先算自己的平均,再对所有请求取中位数;ITL 均值是把所有间隔放在一起取平均。 负载均匀时两者几乎相等;两者一分开,就说明存在少数被拖长的间隔。 本轮 C=1 两个引擎都是均匀的(vLLM 15.83 / 15.83,llama.cpp 5.84 / 5.83); 到 C=8,llama.cpp 分成了 10.30 / 18.33,vLLM 还是 16.63 / 18.25。
2 · 看 p50 得不出任何关于抖动的结论。 一次 200 ms 的停顿混在 25 万个间隔里,不影响中位数,对均值的影响也很小。 本系列的经验是:抖动只在 p99 上露头。 抢占更稀有(万分之一量级),连 p99 都反映不出来,只能看专门的计数器。
3 · 吞吐不是 1 / TPOT。 TPOT 衡量单条请求的出字速度,吞吐衡量整台机器的产能,后者约等于前者乘以并行的请求数。 并发提高时,TPOT 变差、吞吐变好,这正是压测要权衡的交换。
llama.cpp 把每步要读的权重从 14.1 GB 压到 4.7 GB,单流 decode 因此快 2.71 倍, 这个数能用字节账算到小数点后两位。 但并发一上去,优势就没了:两条吞吐曲线在 C≈8 就换位了, 不是第一轮估计的 C≈21。原因不在算力,在批处理红利: C=64 时,vLLM 仍能达到理论地板的 81%,llama.cpp 只有 25%。
单流 decode 步长
5.84 ms
vLLM 15.83 ms。字节账预测 2.99×,实测 2.71×
C=64 批处理效率
25 %
vLLM 81%,是它的 3.2 倍(同一条 roofline)
每多一条并发的边际成本
0.47 ms/tok
vLLM 0.11 ms,理论地板 0.066 ms
C=32 反常塌陷
444 tok/s
比 C=16 的 548 还低;C=64 的 1065 是它的 2.4 倍
第一轮的三条数据瑕疵,两条已消除、一条大幅改善
这一轮的交叉点 C≈8 比第一轮的 C≈21 更可信,因为第一轮那个数被 prompt cache 抬高了。 第一轮的三个问题和本轮的处理:
① llama.cpp 的 prompt cache 默认开着,C=32 有 23% 的请求白拿了 prefill。本轮用 --cache-ram 0 --slot-prompt-similarity 0 关掉了。
② C=32 掉了 8 条请求。本轮只掉 1 条(C=32,319/320)。
③ 只测了 1 / 8 / 32 三个点。本轮补齐到 7 个点。
本轮还补上了显存实测:nvidia-smi 记到 19,089 MiB,与权重加 KV 的推算差 1.5%。
两条线的差别在斜率,不是平移:llama.cpp 起点高、爬得慢,vLLM 起点低、爬得快。 C=8 时两边几乎重合(411 vs 416),C=16 就分开了(681 vs 548)。 这一轮两边都有 7 个点,不用再靠三点插值。
输出吞吐 vs 并发(双对数)
交叉点由 C=8 与 C=16 两点在 log–log 空间插值得到:llama/vLLM 的吞吐比从 1.010 降到 0.804, 在 C = 8.3 处过 1。 注意:llama.cpp 的 C=32 点(444 tok/s)低于它自己的 C=16(548), 这一档是反常的,见「反常」一节。
| 并发 | vLLM tok/s | llama tok/s | 倍数 | vLLM ITL p50 | llama ITL p50 | vLLM TTFT p50 | llama TTFT p50 |
|---|---|---|---|---|---|---|---|
| 1 | 61.9 | 161.5 | 2.61× | 15.83 | 5.84 | 102 | 93 |
| 2 | 118.7 | 242.2 | 2.04× | 16.15 | 6.53 | 154 | 104 |
| 4 | 225.1 | 318.4 | 1.41× | 16.32 | 7.75 | 288 | 189 |
| 8 | 411.4 | 415.6 | 1.01× | 16.63 | 10.30 | 306 | 204 |
| 16 | 681.5 | 548.0 | 0.80× | 17.23 | 23.35 | 474 | 324 |
| 32 | 1026.7 | 443.7 | 0.43× | 19.82 | 61.36 | 588 | 478 |
| 64 | 1387.8 | 1064.8 | 0.77× | 22.50 | 35.32 | 787 | 442 |
单流 decode 是全篇唯一一处理论与实测能对到 10% 以内的地方。 它是纯带宽问题:每生成一个 token,都要把整个模型的权重从显存读一遍。 量化把这些字节压小,步长就该按同样的比例变短。
# 每个 decode step 要读的字节 vLLM bf16 参与矩阵乘的参数 7,070,619,136 × 2 B = 14.14 GB + KV(1 条 × 1152 tok × 56 KiB) = 0.07 GB ────────── 14.21 GB llama Q4_K_M Qwen2.5-7B-Instruct-Q4_K_M.gguf = 4.68 GB + KV(fp16,和 vLLM 同价) = 0.07 GB ────────── 4.75 GB # 字节账预测的步长比 14.21 ÷ 4.75 = 2.99 × # 实测 15.83 ms ÷ 5.84 ms = 2.71 × 达成预测的 91% # 差在哪:反推各自的实际带宽 vLLM 14.21 GB ÷ 15.83 ms = 898 GB/s = 89% MBU llama 4.75 GB ÷ 5.84 ms = 813 GB/s = 81% MBU
2.71 而不是 2.99,差的那 9% 是带宽效率
解量化本身要花掉一点带宽预算(读 4-bit 权重,在寄存器里展开成 fp16 再乘)。 两边用的是同一张卡、同一条 1,008 GB/s 的总线: vLLM 的 decode kernel 把带宽跑到 89%,llama.cpp 的 Q4_K_M 反量化路径跑到 81%。 这 8 个百分点就是 2.99 与 2.71 之间的全部差距。
量化没有骗人,它确实把字节数降到了 1/3; 但 decode 只快 2.7 倍,不是 3.0 倍,因为省下的字节有一小部分被解量化开销吃回去了。
量化省的是读权重的字节,而 prefill 的瓶颈不在读权重:权重读进来一次,要算 1024 个 token。 prefill 是算力密集的(第三节那张 roofline 图量过:prefill 秒能跑到 146 TF/s)。 所以 TTFT 这一栏,llama.cpp 从 C=1 到 C=64 每一档的 p50 都更低:
# TTFT p50(ms),越小越好 C=1 vLLM 102 llama 93 llama 略快 C=8 vLLM 306 llama 204 llama 快(它此时批更小) C=16 vLLM 474 llama 324 C=32 vLLM 588 llama 478 C=64 vLLM 787 llama 442 # 但这是拿吞吐换的:同一档下 llama.cpp 的 ITL 差得多 C=64 vLLM ITL 22.5 ms llama ITL 35.3 ms
上面比的是客户端量到的 TTFT,含调度与网络。 换成两边服务端自己记的纯 prefill 再比一次,结论和幅度都一样。 两条独立路径同向,说明这 9% 不是测量噪声:
# A · 服务端量到的纯 prefill(1024 tok,C=1) llama.cpp prompt eval 85.3 ms 日志中位数,40 条 vLLM request_prefill_time 93.5 ms ← llama 快 8.8% # B · 客户端量到的 TTFT(两边都含调度与网络) llama.cpp TTFT p50 92.7 ms vLLM TTFT p50 102.3 ms ← llama 快 9.4%
这 9% 不是量化的功劳
这 9% 来自实现细节(kernel、框架开销),不是"少读了 3 倍字节"换来的。 prefill 一次前向要算 1024 个 token,权重只读一遍就摊给了所有 token,所以瓶颈在 FLOPs,不在带宽; Q4_K 的解量化只会让 FLOPs 更多。
量化换来的是 decode 速度和显存,不是 TTFT。 对话类负载(短输入长输出)收益大;RAG / 长文摘要(长输入短输出)收益小得多。
批处理的价值在于:一次读进来的权重,给批内所有请求共用。 所以并发翻倍时,单步耗时不该翻倍,只该多出读那些 KV 的时间。 这个下限就是 roofline 地板;地板 ÷ 实测步长,就是批处理效率。
decode 步长:实测 vs roofline 地板
两条地板之间的距离(量化红利)是恒定的,拉开差距的是各自离地板有多远。 vLLM 的实测线始终贴着自己的地板(81%–89%); llama.cpp 从 C=1 的 81% 掉到 C=16 的 24%、C=32 的 11%,C=64 回到 25%, 掉下去容易,回不到 vLLM 那条水平线。 地板 = (权重字节 + 并发 × 1152 tok × 56 KiB) ÷ 1,008 GB/s。
# 理论地板:每多一条并发,每步多读 1152 tok × 56 KiB = 0.066 GB 0.066 GB ÷ 1,008 GB/s = 0.0655 ms / 槽位 # 实测斜率(C=1 → C=64 的 ITL 增量 ÷ 63) vLLM (22.50 − 15.83) ÷ 63 = 0.106 ms / 槽位 地板的 1.6 倍 llama.cpp (35.32 − 5.84) ÷ 63 = 0.468 ms / 槽位 地板的 7.1 倍
这就是"量化省不了调度"的量化版本
llama.cpp 每多接一条并发,单步要多花 0.47 ms;vLLM 只多花 0.11 ms。 相差的 0.36 ms 和权重精度无关,它是 PagedAttention、 continuous batching、CUDA graph 复用这些调度侧工程的价值。
这 0.36 ms 就是「交叉」一节里 C≈8 换位的全部来源。
时间账能读出效率掉得这么快的直接原因。 如果引擎在做 continuous batching,一条请求结束就该立刻补进下一条, 总耗时应该远小于"批数 × 单条端到端时延"。实测正好相反。
# llama.cpp C=32:320 条请求 ÷ 32 个槽位 = 10 批 单条 e2e p50 = 17.85 s 总耗时 ÷ 10 = 179.73 ÷ 10 = 17.97 s ────────── 两者相差 0.7% # 同样的检验用在 C=64 上 单条 e2e p50 = 15.19 s 总耗时 ÷ 8 = 121.61 ÷ 8 = 15.20 s 相差 0.1% # 对照 vLLM C=32 单条 e2e p50 = 7.96 s 总耗时 ÷ 10 = 79.79 ÷ 10 = 7.98 s ← 也吻合,因为负载是定长齐发
总耗时几乎等于"批数 × 单条端到端时延"(吻合到 0.7% / 0.1%), 说明新请求只在整批结束后才成批进场,每 18.0 秒一次。 如果引擎在做 continuous batching,一条结束就补一条,不会有这种整齐的批边界。 换手间隙只有约 0.5 秒(= 实测 TTFT p50 478 ms),占一波的 3%。
注意:本轮没有对 llama.cpp 做 1 Hz 采样, 所以这里只有时间账,没有槽位占用的实测曲线。补采样是「下一步」的第 3 条。
llama.cpp 在高并发下,ITL 的中位数和均值分开了,说明有一批远大于中位数的间隔把均值拉了上去。 如果 decode 是均匀的,这两个数应该几乎相等。
ITL p50 ITL 均值 均值/中位 C=1 5.84 5.83 1.00 完全均匀 C=2 6.53 7.79 1.19 C=4 7.75 11.71 1.51 C=8 10.30 18.33 1.78 C=16 23.35 26.50 1.13 C=32 61.36 67.96 1.11 C=64 35.32 56.25 1.59 # 对照 vLLM,两个数几乎粘在一起 C=64 22.50 42.22 1.88 ← vLLM 也有,来自 chunked prefill 抢步 C=1 15.83 15.83 1.00
TPOT 均值 ÷ ITL p50 − 1 比上面那张表(ITL 自己的均值与中位数)更直接。它量的是
"一条请求的平均出字间隔,比它自己的典型间隔多出了多少",
多出来的部分就是被别人的 prefill 堵住的时间。两个引擎并排:
C llama.cpp vLLM 1 0.2% -0.0% 单流,两边都没有别人可等 2 19.8% 1.0% 4 50.7% 3.4% ← llama 已有一半时间在等,vLLM 还不到 4% 8 78.7% 9.8% 16 46.8% 21.9% 32 89.5% 44.0% 64 105.4% 87.7% ← 到这一档两边都很难看了
差别在于 prefill 是切片插入还是整块插入
llama.cpp 在 C=4 就有一半 decode 时间在等待,vLLM 到 C=8 才 9.8%。
vLLM 开着 enable_chunked_prefill:新请求的 prompt 被切成块,
和正在运行的 decode 混在同一个 step 里,每一步只堵一小会儿。
llama.cpp 是整块插入:一条新请求进来,所有正在 decode 的 slot 都被堵住,
直到它的 1024 个 token 算完。
llama.cpp 在 C=32 只跑出 444 tok/s, 比它自己的 C=16(548)还低;C=64(1065)是它的 2.4 倍。 吞吐曲线的单调性在这里断了。
# 逐槽解码速率 = 1000 ÷ ITL p50 C=1 171.2 tok/s/槽 C=2 153.1 C=4 129.0 C=8 97.1 C=16 42.8 C=32 16.3 ← 比 C=64 还慢 C=64 28.3 # 总吞吐 = 逐槽速率 × 槽数 C=16 42.8 × 16 = 685 (实测 548,因为不是所有槽都满) C=32 16.3 × 32 = 522 (实测 444) C=64 28.3 × 64 = 1811 (实测 1065)
反常的也可能是 C=64 太好,不只是 C=32 太差。 C=64 的 ITL p50(35.32 ms)明显优于 C=32(61.36),roofline 达成率也从 11% 回到 25%, 但它的 CUDA graph 复用率同样只有 1.35%,所以复用率解释不了这次回升。
两种读法都说明:C=32 与 C=64 之间还有一个这一轮没抓到的变量。 所以下面的对照实验要在 C=16 / 32 / 64 三档同时做,不能只重跑 C=32。
三条线索,但还不足以定案
① CUDA graph 复用率整档归零,复用率曲线和逐槽速率的塌陷同步。
算法:服务端每条请求结束时打印累计的 graphs reused;
按并发档切段做差分,再除以该档的 decode 步总数,就得到复用了已捕获 CUDA graph 的 decode 步比例:
| C | 条数 | decode 步总数 | graph 增量 | 复用率 | ITL p50 | roofline 达成率 |
|---|---|---|---|---|---|---|
| 1 | 40 | 10,240 | 10,160 | 99.22% | 5.84 | 80.6% |
| 2 | 59 | 15,104 | 6,487 | 42.95% | 6.53 | 73.1% |
| 4 | 79 | 20,224 | 3,273 | 16.18% | 7.75 | 63.3% |
| 8 | 120 | 30,720 | 1,980 | 6.45% | 10.30 | 50.2% |
| 16 | 200 | 51,200 | 1,457 | 2.85% | 23.35 | 24.4% |
| 32 | 318 | 81,408 | 0 | 0.00% | 61.36 | 11.0% |
| 64 | 512 | 131,072 | 1,773 | 1.35% | 35.32 | 25.0% |
这张表是相关性,不是因果
复用率和 roofline 达成率两列几乎同起同落。 C=32 那一档 81,408 步里一次都没复用,它正是塌得最狠的一档。 C=1 复用 99.22%,几乎每一步都命中,它也是唯一贴住 roofline 的一档。 理论上限是 99.6%,因为每条请求的那一次 prefill 步本来就复用不了。
机制上也说得通:CUDA graph 把几百次 kernel launch 打包成一次提交, batch 形状一变就要重新捕获。vLLM 把 batch padding 到固定的几个桶, 任何实际 batch 都能命中已捕获的图;llama.cpp 的活跃 slot 数每步都在变(30/31/32 来回跳), 图就一直命中不了。
要确认因果,必须做对照实验:用
-DGGML_CUDA_USE_GRAPHS=OFF 重编一份,再扫一遍。
如果关掉图之后 C=1 大幅变慢、C=32 几乎不变,因果链就闭合了。
服务端日志没有给出原因(verbosity 已开到 3)。
另外两条线索
② 唯一一条失败请求也出在这一档(319/320)。
③ 排除项:不是显存(llama.cpp 的 KV 池按 -c 262144 分配,C=32 只用掉一半)、
不是温度(全程 ≤73 °C)、不是 prompt cache(本轮已关)。
在补上对照实验之前,C=32 这一档不应该写进任何结论。 本报告的交叉点用 C=8/16 算,批处理效率引用 C=64,都绕开了这一档。
量化把权重从 14.49 GiB 压到 4.36 GiB,省下 10 GiB,但这一轮的 KV 池又把它吃回去了。
原因是 llama.cpp 开了 -np 64 -c 262144(64 个槽位 × 4096 上下文),KV 池按满配分配。
24 GiB 显存的两种分法
vLLM 侧来自启动日志实测。llama.cpp 侧的总量也是实测:
fingerprint.txt 里 nvidia-smi 记到 19,089 MiB = 18.64 GiB。
只有分段是推算:权重 4.36 GiB + KV(262,144 tok × 56 KiB = 14.0 GiB)= 18.36 GiB,
与实测总量差 1.5%,账是闭合的。
这是一个配置选择,不是引擎的性质
Q4 的显存红利是真的,只是这一轮的启动参数没让它体现出来。
如果把 llama.cpp 的 -c 调成和 vLLM 一样的有效容量,
它的 KV 池会小得多,空出来的显存可以开更多槽位或跑更大的模型。
在"能同时装多少上下文"这一项上,量化确实赢了: vLLM 侧的 6.41 GiB KV 池换算成 120,064 token,llama.cpp 的 262,144 token 是它的 2.18 倍。 只是本轮的负载(1280 tok/请求)用不到这么多。
| 引擎 | 并发 | 请求 | 完成 | 耗时 s | 输出 tok/s | 总 tok/s | TTFT p50 | TTFT p99 | ITL p50 | ITL 均值 | ITL p99 | E2EL p50 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| vLLM | 1 | 40 | 40 | 165.54 | 61.9 | 309 | 102 | 109 | 15.83 | 15.83 | 16.3 | 4,138 |
| vLLM | 2 | 60 | 60 | 129.42 | 118.7 | 593 | 154 | 203 | 16.15 | 16.31 | 16.9 | 4,313 |
| vLLM | 4 | 80 | 80 | 90.99 | 225.1 | 1125 | 288 | 388 | 16.32 | 16.88 | 17.6 | 4,549 |
| vLLM | 8 | 120 | 120 | 74.67 | 411.4 | 2057 | 306 | 734 | 16.63 | 18.25 | 102.1 | 4,978 |
| vLLM | 16 | 200 | 200 | 75.13 | 681.5 | 3408 | 474 | 1407 | 17.23 | 21.01 | 183.7 | 5,850 |
| vLLM | 32 | 320 | 320 | 79.79 | 1026.7 | 5133 | 588 | 2854 | 19.82 | 28.53 | 197.2 | 7,962 |
| vLLM | 64 | 512 | 512 | 94.45 | 1387.8 | 6939 | 787 | 5924 | 22.50 | 42.22 | 207.6 | 11,779 |
| llama.cpp | 1 | 40 | 40 | 63.40 | 161.5 | 808 | 93 | 97 | 5.84 | 5.83 | 6.0 | 1,585 |
| llama.cpp | 2 | 60 | 60 | 62.63 | 242.2 | 1223 | 104 | 189 | 6.53 | 7.79 | 13.1 | 1,859 |
| llama.cpp | 4 | 80 | 80 | 63.50 | 318.4 | 1609 | 189 | 376 | 7.75 | 11.71 | 20.3 | 2,349 |
| llama.cpp | 8 | 120 | 120 | 73.58 | 415.6 | 2086 | 204 | 760 | 10.30 | 18.33 | 104.8 | 3,416 |
| llama.cpp | 16 | 200 | 200 | 92.51 | 548.0 | 2762 | 324 | 1600 | 23.35 | 26.50 | 187.5 | 6,467 |
| llama.cpp | 32 | 320 | 319 | 179.73 | 443.7 | 2261 | 478 | 3206 | 61.36 | 67.96 | 239.7 | 17,851 |
| llama.cpp | 64 | 512 | 512 | 121.61 | 1064.8 | 5376 | 442 | 6268 | 35.32 | 56.25 | 223.8 | 15,193 |
--ignore-eos 严格生效);
llama.cpp 是 250–256 不等(C=1 为 255.95,C=32 为 250.00),
七档合计 336,142 token,比 vLLM 的同口径值少 4,594 个(1.35%)。
吞吐是"输出 token ÷ 墙钟",所以 llama.cpp 侧被低估了约 1.35%。
方向对它不利,但幅度远小于 C≥16 时 20%–57% 的差距,不影响 C≈8 这个交叉点。
做跨引擎对比时,要现场核对这一项,不能假设 --ignore-eos 在两边行为一致。--cache-ram 0 --slot-prompt-similarity 0,
这是第一轮那个 C≈21 交叉点被抬高的原因,本轮已消除。-DGGML_CUDA_USE_GRAPHS=OFF 重编一次,
在 C=16 / 32 / 64 三档上重跑。如果关掉 CUDA graph 之后 C=32 的塌陷消失(或者三档一起变慢但恢复单调),
因果就定了。这是本轮唯一一个悬而未决的问题,优先级最高。-c 调到和 vLLM 等效。
本轮 262,144 的上下文预算让显存对比失去意义。设成
-np 64 -c 81920(64 × 1280)再测一次,才能看到 Q4 真正的显存红利。
这是 Round 2 五份压测报告的第 5 份(llama.cpp 对照)。
整轮的横向结论与生产选型建议见 《选型指南-vllm-llama-cpp》 ——
那份把五组测试的数据合起来,回答"两个引擎各自强在哪、生产上怎么选"。
同目录另有:vllm-01-Qwen2.5-7B并发曲线 · vllm-02-TTFT解剖 · vllm-03-KV的线性 · vllm-04-断崖。