| 配置 | 1 人 | 2 人 | 4 人 | 8 人 | 8 人合計 | 量測工具 |
|---|---|---|---|---|---|---|
| 9B · TP4 · 生產 | 111.5 | 88.9 | 65.0 | 60.5 | 393.0 | v2 |
| 9B · TP2 | 90.5 | 74.7 | 56.0 | 45.3 | 277.0 | v1 · 待重量 |
| 27B · TP2 | 37.2 | 30.6 | 22.5 | 17.0 | 104.5 | v1 · 待重量 |
舊工具的比例並不自洽 ——「每人 × 人數」與「合計」相差達 19%。查出三件事:
① 每個併發等級用的題目不同:PROMPTS[i % 8] 讓 c1 只跑第 0 題、c8 跑滿八題,中英文與長度都不同,曲線因此混進了「題目換了」這個變因。
② prefix caching 偏袒 c1:暖機只跑第 0 題,而那正是 c1 唯一量到的請求。
③ 兩欄不可相乘:每人取的是中位數,合計是總 token ÷ 牆鐘。
v2 讓每個等級跑相同的八題、全部預熱、三輪取中位。修正後「平均 × 人數 ÷ 合計」在 c1–c4 都是 1.00 上下,三輪離散 ≤ 0.8。
但頭條數字沒有被灌高 —— 修正後 c1 反而從 107.5 升到 109–111,因為八題全暖後平均更高。真正被破壞的是曲線形狀(c4 由 67.4 修正為 65.0)與兩欄的一致性。TP2 與 27B 兩列仍是 v1 量的,重量需要停生產的維護窗口。
那一臂在切換前執行,GPU0 還被生產佔著:Free memory on device cuda:0 (0.3/31.73 GiB) —— 排程順序問題,不是 27B TP4 的退化。先前量到的 38.4 tok/s 仍然成立。
max_tokens 太小時,推理模型會把整個額度花在思考段,串流下呼叫端會收到完全空白且無錯誤的回應(實測 200 tokens 時 4/6 空白)。請給 1024 以上。
52.5 tok/s / 每人 — 當時以隨機 token 當提示,掩蓋了輸出退化,而退化的輸出解碼更快,反而灌高數字。基準測試現已內建真實文本的詞彙重複度斷言。
MTP 在自由文本上是淨損,但工具呼叫的輸出高度重複(JSON 骨架、欄位名、<tool_call> 標籤幾乎每次相同)—— 那是 n-gram 命中率最高的地形。因此每個臂同時測工具呼叫與中文散文兩種負載:前者驗證假設,後者當對照組,確認它不會拖累一般對話。三種方法都不需要額外的 draft 模型。
| 方法 | 工具 c1 | 工具 c4 合計 | 散文 c1 | uniq | 備註 |
|---|---|---|---|---|---|
| baseline(無推測) | 50.9 | 92.6 | 71.3 | 0.58 | 對照組 |
| ngram · 3 tokens | 15.7 | 23.6 | 16.5 | 0.68 | 工具 c1 -69.2% · 工具呼叫失敗 |
| ngram · 5 tokens | 15.6 | 45.5 | 19.2 | 0.53 | 工具 c1 -69.4% · 工具呼叫失敗 |
| ngram_gpu · 5 tokens | 16.2 | 44.5 | 25.6 | 0.20 | 工具 c1 -68.2% · 工具呼叫失敗 · 輸出退化 |
| suffix · 5 tokens | — | — | — | — | 缺 arctic-inference 套件,未測 |
散文臂帶退化斷言。ngram_gpu 正是它攔下的:散文 25.6 tok/s 是所有推測臂最高,但輸出已是垃圾 —— 沒有閘門會被記成「最快」。但我後來去驗這道閘門,發現它對中文是瞎的。舊寫法用空白切詞,而中文不用空白,純中文輸出常常只切出一個 token,1/1 = 1.00,看起來滿分健康。在生產上實測校準,四種退化有三種拿到滿分:
| 樣本 | 字元 3-gram | 舊的 uniq-word |
|---|---|---|
| 中文正常輸出 ×3 | 0.92–1.00 | 1.000 |
| 英文正常輸出 | 0.578 | 0.713 |
| 單字重複 300 次 | 0.003 | 1.000 |
| 短句迴圈 | 0.022 | 1.000 |
| 兩句交替 | 0.034 | 1.000 |
| 英文詞迴圈 | 0.008 | 0.008 |
改用字元 3-gram(先去掉所有空白,中英文一視同仁):健康最低 0.578、退化最高 0.034,分離度 17 倍,門檻訂 0.40。這也代表先前那批推測臂裡若有純中文的退化,我根本看不見 —— `ngram_gpu` 是因為英文混雜才被抓到。
vLLM 的 ngram proposer 比對的是精確 token ID 序列,不是「這看起來像 JSON」—— 結構相似不等於 token 序列相同,所以工具呼叫的重複性並不會轉成命中率。而且失敗是正確性問題而非速度問題:三個推測臂都產不出合法的 tool_calls。同樣的量化 KV + ngram 失敗在 RTX 3090 上也有回報(vLLM issue #40831),因此不能歸因於 SM70 世代。
推測臂同時出現「慢三倍」與「工具呼叫失敗」,兩者很可能同源於 TQ2 的 verify / rollback 路徑而非 proposer。五個臂統一用 8192 context —— FP16 KV 在 32K 下裝不進 32GB,不統一就沒有可比性;因此絕對值低於 32K 的 baseline,只有內部比較有意義。
三個量測缺陷,都在我自己的測試工具裡,結論待重測:
① draft / accepted 全為 0,連開了推測的臂也是 —— 代表我抓的 metric 名稱在這個 fork 不存在,整欄沒有訊號,不能讀成「推測沒運作」。
② uniq=1.00 是假的健康,見上方閘門說明。
③ FP16 · 推測關 工具呼叫 0/3 講不通 —— 它沒開推測,理應與 TQ2 · 推測關 的 3/3 一致。這推翻了「責任完全在 TQ2 verify」的乾淨假設,但同樣可能是測試本身的問題。下一步要看實際回應內容,不能再看聚合數字。
| KV / 推測 | 工具 c1 | 工具呼叫 | 散文 | uniq | draft → accepted |
|---|---|---|---|---|---|
| TQ2 · 推測關 | 60.6 | 3/3 | 70.9 | 0.74 | — |
| TQ2 · ngram 3 | 17.1 | 0/3 | 23.1 | 0.75 | — |
| FP16 · 推測關 | 54.5 | 0/3 | 68.3 | 1.00 | — |
| FP16 · ngram 3 | 19.3 | 0/3 | 16.4 | 0.78 | — |
| TQ2 · ngram 3 · eager | 11.6 | 0/3 | 11.2 | 1.00 | — |
前三個假設都被實測推翻,而且它們都很有說服力 —— 留在這裡是為了不再重走。真正的轉折是不再靠推論、改看哪一套帳本對不上:當 cuda_reserved 與程序 RSS 同時持平、系統可用記憶體卻持續下降,這個組合只指向一個地方。
| 修法 | 修法前 | 修法後 | 說明 |
|---|---|---|---|
| num_anchors 512 → 128 | 跑不到第一個 micro-step | 穩定推進 | Q_LEN 3584 → 896,scores 面積少 6.2 倍。硬體逼出的偏離,τ 需註明在 128 anchors 下測得。 |
| expandable_segments | 49→99 步流失 12.3GB | 流失 1.7GB | 治裝置端碎片。有效但不足以收斂。 |
| pin_memory=False | 步 464 剩 12.3GB 後陣亡 | 步 900 仍有 37.4GB | 治主機端釘選快取。GB10 主機與裝置共用實體 RAM,釘選換不到 DMA 好處 —— 每步耗時反而從 5.0s 降到 2.53s,全程預估從 19 小時縮到 9.4 小時。 |