每一列都是量過的,沒有推算值。併發欄是同時的請求數,tok/s 是每人而非合計。「參照」列是別的引擎或別台機器,只用來對照,不是我們的產出。
| 模型 · 量化 | 拓樸 | context | 併發 | tok/s | 時期 | 註記 |
|---|---|---|---|---|---|---|
| 9B · Q4_K_M | TP4 | ~200 tok | 1 | 152.8 | 現行 | 現行生產 |
| 9B · Q4_K_M | TP4 | ~200 tok | 8 | 76.8 | 現行 | 每人 |
| 27B · IQ4_XS | TP4 | ~200 tok | 1 | 68.2 | 現行 | 64K 配置 |
| 27B · IQ4_XS | TP4 | ~200 tok | 8 | 20.0 | 現行 | 每人 · 合計 160 |
| 27B · IQ4_XS | TP4 | ~8K tok | 8 | 17.7 | 現行 | 每人 |
| 27B · IQ4_XS | TP4 | ~28K tok | 8 | 14.1 | 現行 | 每人 |
| 27B · IQ4_XS | TP4 | 105,856 tok | 1 | 37.7 | 現行 | 128K 配置 |
| 27B · IQ4_XS | TP4 | 105,856 tok | 8 | 7.0 | 現行 | 每人 · 端到端只有 3.4 |
| 27B · Q4_0 | TP4 | ~200 tok | 1 | 75.1 | 現行 | requant · 舊版權重 |
| 27B · Q4_0 | TP4 | ~200 tok | 8 | 34.5 | 現行 | 每人 · 合計 276 · 唯一過 30 的 |
| 27B · IQ4_XS | TP4 | ~200 tok | 1 | 30.2 | 現行 | 開 MTP spec-1 · 淨損 |
| 27B · Q4_K_S | TP4 | ~200 tok | 24 | 33.6 | 現行 | 每人 · 合計 241 · util 0.75 · 不開 MTP |
| 27B · Q4_K_S | TP4 | ~200 tok | 32 | 33.7 | 現行 | 每人 · 合計 256 · c8 到 c32 是平的 |
| 27B · IQ4_XS + 批次 MMVQ | TP4 | 混合 · 100–400 out | 1 | 69.1 | 現行 | <b>不開 MTP</b> · 異質 p50 · 這是目前最佳單人 |
| 27B · IQ4_XS + 批次 MMVQ | TP4 | 混合 · 100–400 out | 4 | 48.5 | 現行 | 不開 MTP · 異質 p50 · 合計 108 · 改前 36.74 |
| 27B · IQ4_XS + 批次 MMVQ | TP4 | 混合 · 100–400 out | 8 | 32.3 | 現行 | 不開 MTP · 異質 p50 · <b>合計 143</b> · 改前 20.58 |
| 27B · IQ4_XS | TP4 | 混合 · 100–400 out | 8 | 20.6 | 現行 | 不開 MTP · 未改 kernel · 這是被修掉的那條 |
| 27B · IQ4_XS + MTP1 + 新 kernel | TP4 | ~200 tok | 8 | 15.1 | 現行 | 每人 · 合計 103 · capture 對齊 + <code>VLLM_GGUF_IQ4XS_MMQ=64</code> |
| 27B · IQ4_XS + MTP1 + 新 kernel | TP4 | ~200 tok | 16 | 12.5 | 現行 | 每人 · 合計 188 |
| 27B · IQ4_XS + MTP1 + 新 kernel | TP4 | ~200 tok | 32 | 9.0 | 現行 | 每人 · <b>合計 266</b> · 同構上界 |
| 27B · IQ4_XS + MTP1 + 新 kernel | TP4 | 混合 · 100–400 out | 8 | 15.1 | 現行 | <b>異質</b> p50 · p95 18.08 · 合計 61.6 · TTFT p50 0.51s |
| 27B · IQ4_XS + MTP1 + 新 kernel | TP4 | 混合 · 100–400 out | 32 | 9.2 | 現行 | <b>異質</b> p50 · p95 10.05 · <b>合計 136.7</b> · TTFT p50 1.80s · 32/32 完成 |
| 27B · IQ4_XS + MTP1 | TP4 | ~200 tok | 24 | 8.2 | 現行 | capture 對齊 <code>(1+draft)×席次</code> · 合計 181 · 未開新 kernel |
| 27B · IQ4_XS + MTP1 | TP4 | ~200 tok | 32 | 7.6 | 現行 | 同上 · 合計 225 |
| 27B · IQ4_XS + MTP1 | TP4 | ~200 tok | 24 | 3.8 | 現行 | capture 未對齊 · 掉出 CUDA graph 走 eager · 這是被修掉的缺陷 |
| 27B · Q4_K_M | TP4 | 105,856 tok | 1 | 35.1 | GDN 開 | 8/7 舊量測 |
| 27B · Q4_K_M | TP4 | 105,856 tok | 8 | 8.1 | GDN 開 | 8/7 舊量測 |
| 9B · Q4_K_M | TP4 | ~200 tok | 1 | 123.1 | GDN 開 | 8/6 之前 |
| 9B · Q4_K_M | TP2 | ~200 tok | 1 | 96.2 | GDN 開 | |
| 9B · Q4_K_M | 單卡 | ~200 tok | 1 | 69.7 | GDN 開 | |
| 9B · Q4_K_M | TP4 | ~28K tok | 1 | 106.2 | GDN 開 | |
| 9B · Q4_K_M | TP4 | ~28K tok | 8 | 45.9 | GDN 開 | 每人 |
| 27B · Q4_0 | TP4 | ~200 tok | 1 | 60.4 | GDN 開 | |
| 27B · Q4_0 | TP4 | ~200 tok | 8 | 30.9 | GDN 開 | 每人 |
屋頂線:27B IQ4_XS 是 15.13 GB,單卡 900 GB/s 給出 59.5 tok/s 的單 token 上限,四卡 238。llama.cpp 單卡不開推測拿到 65%,開 MTP 拿到 94% —— 單卡幾乎榨乾了。我們的 vLLM 四卡在 29%,差距不在頻寬。
這張表刻意和上面分開。先前它們混在同一張表裡、只用一個小標籤標「參照」,結果 3090 那列的 85 被讀成我們的 27B 成績。我們自己的 27B 在 vLLM 上的最高紀錄是 75.1(Q4_0 · TP4 · KV turboquant_2bit_nc · ~200 tok · c1),本機所有 log 裡沒有更高的。
| 模型 · 量化 | 引擎 · 拓樸 | context | 併發 | tok/s | 註記 |
|---|---|---|---|---|---|
| 27B · IQ4_XS | llama.cpp · 單卡 | ~200 tok | 1 | 39.24 | 不開推測 |
| 27B · IQ4_XS | llama.cpp · 四卡 layer | ~200 tok | 1 | 38.46 | 等於單卡,管線切分買容量不買速度 |
| 27B · IQ4_XS | llama.cpp · 四卡 row | ~200 tok | 1 | 22.12 | pp512 只有 88.99,比單卡慢十倍,病態 |
| 27B · IQ4_XS | llama.cpp · 四卡 tensor | ~200 tok | 1 | 33.61 | pp512 2333 對單卡 855,提示處理 2.7 倍 |
| 27B · IQ4_XS | llama.cpp · 單卡 + MTP n=3 | ~200 tok | 1 | 55.90 | MTP 在 llama.cpp 會賺,在 vLLM 不會 |
| 27B · IQ4_XS | llama.cpp · 單卡 | 24,704 tok | 1 | 33.40 | f16 KV |
| 27B · IQ4_XS | llama.cpp · 單卡 | 24,704 tok | 1 | 25.50 | tbq3 KV,反而更慢 |
| 27B · AutoRound INT4 | 外部 · vLLM · 3090 單卡 | 125K tok | 1 | 85.00 | 別人的機器,不是我們的成績 |
llama.cpp 四卡的三種模式都驗過每張卡真的吃到權重(tensor 是 4519 / 4313 / 4313 / 4313 MiB),不是只看旗標有沒有被接受 —— 第一次量的時候 -ts 用了逗號,llama-bench 把它讀成「掃多組設定」,四個臂其實都跑在 GPU 0,整批數字作廢重跑。
27B Fable · TP4 · 4×V100-SXM2-32GB NV2 · KV turboquant_2bit_nc(K2V2)· max-model-len 65536 · util 0.75 · kv-splits 96 · block-size 32 · batched-tokens 16384 · max-num-seqs 8 · prefix caching 開 · MTP 關 · GDN full-forward 關 · 模型 IQ4_XS 14.09 GiB · 每卡 23.8 GB / 32
| 提示 | 併發 | 冷 TTFT | 暖 TTFT | 每人 tok/s | 端到端 tok/s | prefix 命中 |
|---|---|---|---|---|---|---|
| ~200 | c1 | 0.27s | 0.27s | 68.2 | 43.8 | 0% |
| ~200 | c4 | 0.27s | 0.82s | 32.8 | 66.9 | 0% |
| ~200 | c8 | 0.27s | 0.69s | 20.0 | 101.1 | 0% |
| ~8K | c1 | 4.19s | 0.96s | 63.9 | 25.3 | 85% |
| ~8K | c4 | 4.19s | 4.10s | 29.6 | 57.7 | 85% |
| ~8K | c8 | 4.19s | 6.62s | 17.7 | 60.1 | 85% |
| ~28K | c1 | 25.33s | 1.79s | 55.3 | 40.2 | 97% |
| ~28K | c4 | 25.33s | 6.68s | 25.1 | 47.8 | 97% |
| ~28K | c8 | 25.33s | 12.90s | 14.1 | 58.9 | 97% |
27B Fable · TP4 · KV turboquant_2bit_nc · max-model-len 131072 · util 0.60 · kv-splits 160 · block-size 64 · batched-tokens 65536 · max-num-seqs 8 · prefix caching 開 · MTP 關 · 模型 IQ4_XS · 每卡 13.5 GB / 32
| 提示 | 併發 | 冷 TTFT | 暖 TTFT | 每人 tok/s | 端到端 tok/s |
|---|---|---|---|---|---|
| ~200 | c1 | 0.26s | 0.26s | 67.9 | 45.2 |
| ~200 | c8 | 0.26s | 0.69s | 20.0 | 98.1 |
| ~105K | c1 | 209.08s | 16.35s | 37.7 | 2.1 |
| ~105K | c8 | 209.08s | 130.03s | 7.0 | 3.4 |
兩欄不一樣:「每人 tok/s」是 token 開始吐之後的速率,「端到端」把 TTFT 也算進去。105K 那兩列端到端只有 2.1 / 3.4,因為 256 個 token 前面卡著 209 秒的冷 prefill。那格的體感由 TTFT 決定。對照 8/7 的 Q4_K_M:冷 390.9s、暖 c1 31.1s —— 新模型的 prefill 快了 47%。
27B Fable · TP4 · 4×V100-SXM2-32GB NV2 · KV turboquant_2bit_nc(K2V2)· max-model-len 65536 · util 0.75 · kv-splits 96 · block-size 32 · batched-tokens 16384 · max-num-seqs 8 · prefix caching 開 · MTP 關 · GDN full-forward 關 · 提示 ~200 tok · 每個臂在同一個伺服器內重測 c1 當錨
| 併發 | IQ4_XS 每人 | IQ4_XS 步時間 | Q4_0 每人 | Q4_0 步時間 |
|---|---|---|---|---|
| c1 | 68.2 | 14.7 ms | 75.1 | 13.3 ms |
| c2 | 47.6 | 21.0 ms | 51.6 | 19.3 ms |
| c4 | 32.6 | 30.7 ms | 34.6 | 28.4 ms |
| c8 | 20.0 | 50.0 ms | 34.5 | 28.5 ms |
IQ4_XS 的步時間精確符合 11.3 ms + 4.83 ms × 序列數 —— c4 代入 30.7(實測 30.7)、c8 代入 50.0(實測 50.0)。而 Q4_0 從 c4 到 c8 多了四個序列,步時間只多 0.1 ms,已經飽和。
那條直線是 i-quant 專屬的病,不是這台機器的性質:IQ4_XS 用非均勻碼表,每多一列都要重新查表拆包;Q4_0 是均勻 4-bit 分塊,批次幾乎免費。
不是頻寬:4.83 ms 折算成流量是每序列每步 17 GB,比整顆 14.09 GiB 的模型還大。
GDN_DECODE_FLASHQLA、MIXED_QKV_CONTIGUOUS+Z_CONTIGUOUS、block-size 64、capture [1,2,4,8,16]+max-num-seqs 16 —— 四個臂的 c1/c2/c4/c8 一位小數都不差。不是效果小,是零效果。
我對這件事給過三個機制,三個都被自己的量測推翻:① GDN 遞迴狀態頻寬 —— 算出來 0.056 ms,差三個數量級;② GDN 對序列迴圈 —— profiler 顯示 GDN kernel 在 c8 的啟動次數是 c1 的 1.02x,沒有迴圈;③ ticket18 中等批次 kernel —— 開與關量出 34.48 對 35.13,關掉還略好。
真正有效的變動只有一個:換量化型別。profiler 指出主力 kernel 是 mul_mat_vec_q,那是矩陣×向量、為 batch 1 設計的,而 c8 仍然走它。
27B Fable · TP4 · 4×V100-SXM2-32GB NV2 · KV turboquant_2bit_nc(K2V2)· max-model-len 65536 · util 0.75 · kv-splits 96 · block-size 32 · batched-tokens 16384 · max-num-seqs 8 · prefix caching 開 · MTP 關 · GDN full-forward 關 · 模型 IQ4_XS · --spec-method mtp · c1
| 推測 | 散文 | 工具呼叫形狀 |
|---|---|---|
| 關 | 68.1 | 68.4 |
| spec-tokens 1 | 29.7 | 30.2 |
| spec-tokens 2 | 21.1 | 25.7 |
內容類型沒有救到它 —— 工具呼叫那格和散文一樣爛。llama.cpp 上 85–99% 的接受率在 vLLM 這裡不轉換成速度,因為瓶頸是推測步本身的成本。另外 temp 0 之下三個臂的生成長度不同(272 / 271 / 201),推測路徑會改變輸出,那比速度更嚴重。
後來的六臂掃描顯示單人接受率從 2-bit 到 f16 完全不動(51.5 / 50.0 / 52.3 / 51.5 / 51.5%)。下面的數字是真的量到的,但那是單一提示的單次量測,我把它寫成了因果。保留原文備查。
27B Fable · TP4 · 4×V100-SXM2-32GB NV2 · KV turboquant_2bit_nc(K2V2)· max-model-len 65536 · util 0.75 · kv-splits 96 · block-size 32 · batched-tokens 16384 · max-num-seqs 8 · prefix caching 開 · MTP 關 · GDN full-forward 關 · 模型 IQ4_XS · --spec-method mtp --spec-tokens 3(對照組用的深度)· max-model-len 32768 · c1 · /no_think
| KV 格式 | 散文 | 程式碼 | JSON |
|---|---|---|---|
turboquant_2bit_nc(我們一直在用) | 14.7% | 34.9% | 47.0% |
turboquant_3bit_nc(對照組用的) | 31.1% | 58.4% | 87.1% |
| 逐位置(JSON) | 我們 2-bit | 我們 3-bit | 外部對照組 |
|---|---|---|---|
| position 0 | 80.6% | 97.6% | 97% |
| position 1 | 44.4% | 90.4% | 95% |
| position 2 | 16.1% | 73.5% | 91% |
MTP 的 draft 頭從 hidden state 預測下一個 token,而 hidden state 來自對 KV cache 的注意力。2-bit 把餵給它的東西壓爛了 —— 這不會讓輸出變差(target 每個 token 都會驗),它讓草稿變爛。愈後面的位置愈依賴前面草稿推出的狀態,所以 position 2 塌得最兇。
而我們負擔得起 3-bit:見下面的容量表,3-bit 在 64K 仍有 64.65 倍併發餘裕,而我們只需要 8。2-bit 換到的是我們根本用不到的容量,代價是 MTP 的接受率。
還沒解決的:即使 87.1% 接受率,解碼仍然只有 22.65 對不開推測的 68。接受長度算起來約 3.5,理論上該有 1.67 倍加速。接受率修好了,推測機制本身還是虧的 —— 這兩件現在乾淨分開了。
27B IQ4_XS · max-model-len 65536 · util 0.75 · block-size 32 · 只讀引擎啟動時報的 pool,不做生成 · 此模型 head_count_kv=4,vLLM 按 head 切,TP4 是分散上限
| 拓樸 | KV pool | 64K 下併發倍數 | 每卡記憶體 |
|---|---|---|---|
| TP1 | 370,085 tok | 5.65x | 20.8 GB(只有卡 0) |
| TP2 | 2,174,253 tok | 33.18x | 23.0 / 22.8 |
| TP4 | 5,751,747 tok | 87.76x | 23.8 × 4 |
KV 確實分散,而且是超線性:TP1 → TP4 是 15.5 倍不是 4 倍,因為權重也跟著切,每張卡騰出更多空間給 KV。llama.cpp 四卡 layer 也分散(5439 / 5027 / 5233 / 6049 MiB,單卡是 19177),它按層切,不受 head 數限制。
| KV 格式 · TP4 | KV pool | 64K 下併發倍數 |
|---|---|---|
turboquant_2bit_nc | 5,751,747 | 87.76x |
turboquant_3bit_nc | 4,237,044 | 64.65x |
turboquant_4bit_nc | 3,480,429 | 53.11x |
f16(auto) | 1,088,046 | 16.60x |
TurboQuant 2-bit 給的併發是 f16 的 5.3 倍。這就是為什麼 KV 格式必須寫進每一份配置 —— 它直接決定能放多少人。
27B Fable · TP4 · 4×V100-SXM2-32GB NV2 · KV turboquant_2bit_nc(K2V2)· max-model-len 65536 · util 0.75 · kv-splits 96 · block-size 32 · batched-tokens 16384 · max-num-seqs 8 · prefix caching 開 · MTP 關 · GDN full-forward 關 · 提示 ~200 tok · 三個臂都是現行的新合併版,只差量化型別
| 量化 | c1 | c2 | c4 | c8 | 批次增益 |
|---|---|---|---|---|---|
| Q4_K_S | 68.8 | 52.2 | 33.9 | 34.4 | 4.00x |
| Q4_K_M | 66.4 | 46.9 | 33.3 | 31.1 | 3.75x |
| IQ4_XS | 68.4 | 47.8 | 32.1 | 19.9 | 2.33x |
| Q4_0(舊版權重的 requant) | 75.1 | 51.6 | 34.6 | 34.5 | 3.74x |
Q4_K_S 是答案:同一顆新模型、單人不輸(68.8 對 68.4)、c8 從 19.9 拉到 34.4,超過 30 的目標,不必退回舊權重。
機制有兩個獨立佐證。派發器裡三條中等批次路徑全部要求 qweight_type == 2(Q4_0),i-quant 一條都進不去;而微基準顯示 ggml_mul_mat_a8 對 IQ4_XS 根本沒在算 —— M=2 到 M=512 的耗時全是 0.03 ms 不動,換算 3000 TFLOPS,V100 峰值只有 125,物理上不可能,那條路徑是空轉返回。
所以 i-quant 在批次時只能留在逐列的向量路徑,這就是那條 11.3 ms + 4.83 ms × 序列數 的來源。
9B Q4_K_M · TP4 · KV turboquant_2bit_nc · max-model-len 65536 · util 0.30 · block 32 · batched 16384 · max-num-seqs 32 · MTP 關 · 對線上生產直接量,未停機
| 併發 | capture [1,2,4,8](原本) | capture [1,2,4,8,16,32](現行) |
|---|---|---|
| c1 | 152.65 | 152.4 |
| c8 | 76.3 / 合計 607 | 76.3 / 合計 556 |
| c16 | 14.5 / 合計 232 | 61.0 / 合計 899 |
| c24 | — | 40.4 / 合計 897 |
| c32 | — | 39.6 / 合計 1188 |
CUDA graph 是把一步解碼的幾千次 kernel 發射錄成一份、之後一次重播,但錄影綁死批次形狀。捕捉集合只到 8,第 9 個併發就沒有東西可以墊,整步退回 eager。
代價量過了:+34 MiB/卡、+31 秒啟動,c1–c8 完全沒退化,工具呼叫每個臂 ALL_PASS。已上線,回滾是 cp fable9b_start.sh.bak_capture 再重啟。
這個不一致是 8/6 我自己造成的 —— 那天把 max-num-seqs 從 8 調到 32,只驗了 c1 和 c8,沒量 c16。調了上限就要量到上限。
權重 17408 × 5120(此模型 ffn 形狀)· Q4_0 · sm_70 · cuda event 計時 30 次取平均 · 每版都先過正確性再看時間
| 版本 | 改了什麼 | M=16 |
|---|---|---|
| v1 | 一 warp、逐位元組拆 nibble | 1.19 |
| v2 | 四 warp、N_TILE 64(委員會的分塊) | 0.63 |
| v3 | 全執行緒參與、K_TILE 128、half2 | 0.51 |
| v4 | 位元技巧解量化(免轉換指令) | 0.44 |
| v5 | M_TILE 64、多累加器 | 0.72 |
| dp4a(要打敗的) | — | 0.331 |
| 屋頂線 | 50.2 MB ÷ 900 GB/s | 0.056 |
五版全部正確(max abs err 0.0000),但沒有一格贏過。而且時間對 M 幾乎不變(v4 在 M=1 是 0.439、M=16 是 0.437)—— tensor core 是免費的,成本全在把權重物化成 fp16。
原因:WMMA 的 load_matrix_sync 只能從記憶體載入 fragment,所以解量化結果一定要先寫進 shared memory,我因此多搬了 178 MB。dp4a 在暫存器裡邊解邊乘,完全不物化。Marlin 那類能贏的 kernel 用的是原始 mma.sync PTX 手工排暫存器,繞過這個限制。
所以「用 WMMA C++ API 做融合量化 GEMM」這條在這個問題上是死的 —— 不是調得不夠好。這個否定結論擋掉一整個方向,值得留著。
微基準顯示 M=64 時 dp4a(1.290)已經輸給 dequant+cuBLAS(0.787),而門檻 DEQUANT_CUBLAS_MIN_ROWS 是 256,看起來 48–255 那段跑在慢路上。實測三個門檻:256 與 48 每格相同(解碼 M ≤ 32、prefill 分塊 ≫ 256,那段碰不到),而 16 讓 c16 掉 39%(解碼批次被推去走解量化路徑)。維持 256,這條劃掉。
27B · llama.cpp llama-batched-bench · -c 32768 -b 2048 -ub 512 · -npp 256 -ntg 128 · KV f16 · 欄位是 aggregate 生成吞吐(B 是同時在跑的序列數)
| 拓樸 | B=1 | B=2 | B=4 | B=8 |
|---|---|---|---|---|
| IQ4_XS · 單卡 | 38.79 | 63.00 | 86.24 | 110.55 |
| Q4_K_S · 單卡 | 34.45 | 56.58 | 68.96 | 72.97 |
IQ4_XS · 四卡 layer | 38.27 | 62.39 | 88.57 | 111.75 |
IQ4_XS · 四卡 tensor | — | — | — | 241.96 |
Q4_K_S · 四卡 tensor | — | — | — | 209.58 |
我寫過「四卡對 llama.cpp 的解碼沒有幫助」。那是 batch 1 量的。八個序列同時在跑時,tensor 是單卡的 2.2 倍(241.96 對 110.55),提示處理更是 1828 對 690。layer 確實不縮放(111.75 對 110.55),那部分成立。
在我們的 vLLM fork,IQ4_XS 在 8 併發塌到 19.9 而 Q4_K_S 是 34.4。在 llama.cpp 剛好相反 —— 同一顆檔案,IQ4_XS 在 B8 是 110.55、Q4_K_S 只有 72.97,i-quant 快 51%。
所以 i-quant 不是天生不適合批次,是我們的 GGUF 載入路徑沒有實作:微基準顯示 ggml_mul_mat_a8 對 IQ4_XS 空轉返回(M=2 到 512 耗時全是 0.03 ms,換算 3000 TFLOPS,V100 峰值 125)。參考實作就在同一台機器上的 llama.cpp 裡,這把「自己寫 kernel」變成「從上游搬」。
8/10 早上就量到 MTP 在 llama.cpp 賺 44%(38.8 → 55.9)、在 vLLM 虧。我接著花了一整天修 vLLM 那個虧的,而不是去看那條會賺的路能不能撐起產線。把 vLLM 當預設答案的理由只是「生產在上面」,那不是技術理由。
來源也該分清楚:逐位置 97/95/91% 那組是那篇 3090 的 vLLM 紀錄;模型作者 DavidAU 自己的 MTP 數字是 llama.cpp 的(75 → 90+ t/s)。兩個都在,而我只拿前者當標竿。
同一顆 27B IQ4_XS · llama.cpp llama-server · -c 65536 -np 8 -sm layer · KV f16 · --spec-type draft-mtp --spec-draft-n-max 3 · 對照組是 vLLM Q4_K_S · TP4 · KV turboquant_2bit_nc · maxlen 65536 · 不開 MTP
| 併發 | llama.cpp + MTP 每人 | 合計 | 接受率 | vLLM 每人 | 合計 |
|---|---|---|---|---|---|
| c1 | 66.53 | 57.30 | 80.0% | 69.6 | — |
| c4 | 30.57 | 110.07 | 80.0% | 34.1 | 136 |
| c8 | 17.24 | 123.05 | 80.0% | 34.3 | 275 |
單人從 38.27 拉到 66.53(+74%),接受率 80%。技術本身沒問題,問題是即使開了有效的 MTP,也只追平 vLLM 不開推測的水準,而八併發差兩倍以上。
llama.cpp 唯一會縮放的拓樸是 tensor(batched-bench 量到 B8 241.96 合計),但 llama-server 不支援它:llama_params_fit is not implemented for SPLIT_MODE_TENSOR, abort —— 伺服器起得來、健康檢查會過、但產不出任何內容。所以那個數字不可服務,而且它還是不開 MTP 的。
產線留在 vLLM,現在是結論不是預設。先前「留在 vLLM」的理由只是生產在上面,那不是技術理由。