量化會不會加速,取決於你怎麼寫 kernel

同一份 int4 KV cache 量化,eager 版相對 SRHT 慢 12–17%,把四步融成兩顆 Metal kernel 後追平;端到端對 fp16,Gemma 短前綴快 8.2%,但 Qwen 2K 前綴只吐兩個 token。差別在四步拆開呼叫,還是融成兩顆——以及平台、前綴與架構的前提。

15 分鐘Read in English
quantizationkv-cacheapple-siliconinference
標題「量化會不會加速,取決於你怎麼寫 kernel」的技術筆記封面

數字全部來自 arXiv 2605.05699v1(2026-05-07)。作者是 Illumina 的 Mohamed Amine Bergach;這是單人預印本,未同儕審查。我們沒有在 Apple M1 上獨立重跑;「我認為」的地方會標出來。

先講一個畫面

你在筆電上跑 1B 模型,context 從 256 拉到 4K,解碼變慢、KV cache(解碼時累積的 K/V 快取)越長越吃記憶體。這篇要幫你看懂兩組數字:那個「慢 17%」跟「快 8%」分別在比什麼。還有下次看 benchmark 要問哪四件事,以及什麼時候該留 fp16 退路。

上網查,答案通常是「量化」。但下一句總是「會變慢」。

有一篇論文把這句話拆開了。同一份 int4(4-bit 整數)KV cache 量化,eager 版要四次 kernel 啟動(dispatch)。在 360M 模型上,這讓延遲比 SRHT(用 Hadamard 做隨機旋轉的對照法)高 17%。把四步融成兩顆 kernel 後,SmolLM 的同一組對照變成低 1%,1.7B 低 5%;Qwen 仍慢 1%。端到端對 fp16,Gemma-3 1B 在 256 前綴低 8.2%。

但先別高興。換到 Qwen2.5-1.5B 的 2K 前綴,它只吐兩個 token 就 EOS(提前結束)。這 8% 有平台、前綴、架構的前提,後面會把邊界畫出來。

這篇要把兩個常被混在一起的問題拆開。一個是實作稅:同一個演算法,eager 版四次 dispatch、融合版兩顆 kernel。另一個是部署帳:int4 cache 對 fp16,端到端到底快多少、什麼時候會壞。

先看那個「慢 17%」

論文的方法叫 rotate-then-quantize。KV cache 裡的每個 K/V 向量,先做一個隨機旋轉,再壓成 int4;attention 讀取時,再解量化、反旋轉回來。

這裡的旋轉是 SRFT(sign-randomized FFT):先乘上一組固定的隨機正負號,再做 FFT。目的是打散離群值。模型的 K/V 向量裡,少數很大的值會把量化尺度撐大,其他值就被壓成 0;先旋轉,讓每個座標看起來接近高斯分布,均勻量化才不會被少數大值拖垮。

問題在「四步」怎麼寫。eager 版把旋轉、量化、解量化、反旋轉拆成四個 PyTorch/MPS 呼叫;每一層、每一個解碼步,就是四次 kernel dispatch。

dispatch 是什麼?就是 GPU 每啟動一顆 kernel 的排隊與搬資料成本。計算本身可能只有幾微秒,但四次啟動、四次把資料搬進搬出,加起來就變成主成本。

論文量到的代價是:SmolLM2-360M 延遲高 17%、1.7B 高 16%、Qwen2.5-1.5B 高 12%。基準是 SRHT(sign-randomized Hadamard transform),另一種隨機旋轉,不是 fp16。

為什麼拿 SRHT 當基準?因為 SRFT 和 SRHT 的量化品質在誤差內,差別只在「旋轉用 FFT 還是 Hadamard」。拿它對照,才能把「旋轉本身的成本」和「四步拆開的實作稅」分開。

論文有一句話是整段的骨架,我照原意翻:

成本在 dispatch,不在計算。底層的 rfft 和 irfft 各只要個位數微秒,但四步分開呼叫,把這個開銷乘了上去。

這就是為什麼同樣的演算法,換一個寫法會從 +17% 變成 −1%。不是量化變好了,是四次排隊變成兩次(如圖 1)。

圖 1:同一份量化,eager 版每層每步四次 dispatch;融合版把前兩步與後兩步各收成一顆 kernel。

把四步融成兩顆,還不夠

融合的做法,是把前兩步和後兩步各自塞進一顆 Metal kernel(Apple 的 GPU 程式):forward 做旋轉加量化,inverse 做解量化加反旋轉。資料在中間不用回 device memory,留在 GPU 內部的快速記憶體。

同一組對照,SmolLM 反轉,Qwen 仍慢 1%:

模型SRHT 基準eager Δ融合 SRFT(Δ)
SmolLM2-360M37.4 ms/tok+17%37.0 ms/tok(−1%)
SmolLM2-1.7B222.2 ms/tok+16%211.9 ms/tok(−5%)
Qwen2.5-1.5B84.9 ms/tok+12%85.7 ms/tok(+1%)

(Δ 相對 SRHT,百分比取整;256 token 前綴加 128 token 生成;Apple M1。不是對 fp16。)

1.7B 的差距比 360M 大。eager 的罰則在兩個模型上差不多(+17% 對 +16%)。差別在融合後:1.7B 每層的 KV 維度是 2048,360M 只有 320。融合 kernel 的固定啟動成本被更多工作攤薄,所以它從 −1% 變成 −5%。

到這裡,故事看起來很乾淨:四步變兩顆,慢 17% 變快 1%(360M)。但如果你看到這裡就下結論「量化一定快」,會漏掉下一段。

端到端對 fp16 的 −8.2%,不是只有融合。它還需要三個條件。

第一,量化配方。 4-bit 如果只用 per-token(每個 token 一個尺度),Qwen 的 ΔPPL 會炸到 +7975。作者改成 per-channel λ 加 per-group abs-max(每個 channel 一個縮放係數,每 32 個值一組取絕對值最大),把同一個數字壓到 +638.6。這個配方是在同一顆融合 kernel 裡做的。

第二,dequant-prefix cache。 解量化過的前綴先存起來,不要每一步重算;最近 16 個 token 保留 fp16,只有這個視窗滿了才失效。沒有它,Qwen 在 1024 前綴是 +2.4% 慢;有了它,才變成 −1.6% 快(表內回算;論文文字寫 −0.7%)。

第三,模型架構。 Gemma-3 1B 有 sliding-window 層:大部分層只看固定視窗、用 fp16,只有少數 full-attention 層(看完整前綴)要付 dequant 成本。所以它的優勢可以撐到 4096;Qwen 是 28 層 full-attention,成本隨前綴線性成長。

把這三件事放回去,端到端的表長這樣:

模型前綴fp16 → int4(ms/tok)延遲差
Gemma-3 1B25671.2 → 65.4−8.2%
Gemma-3 1B102483.3 → 82.5−0.9%
Gemma-3 1B2048129.5 → 121.3−6.3%
Gemma-3 1B4096188.5 → 182.4−3.2%
Qwen2.5-1.5B25690.7 → 88.3−2.6%
Qwen2.5-1.5B1024137.1 → 134.9−1.6%

(Δ 相對 fp16;Apple M1;256 token 前綴加 128 token 生成。Qwen 2048 不在表裡,原因見下一節。Qwen 1024 的 −1.6% 是用表內 137.1 → 134.9 回算;論文文字寫 −0.7%,我們以表內數字為準。)

記憶體方面,Qwen 是 3.0–3.1×;Gemma 的 19.5× 不是同層比較(full-attention 層內約 3.2×),不能直接跟 Qwen 比。

−8.2% 是 256 前綴的最佳情況。同一個模型拉到 4096,只剩 −3.2%。Qwen 拉到 1024,只剩 −1.6%。「量化快 8%」不是一個常數,是一條會隨前綴長度變化的曲線。

快之前,先看它掉了多少

速度之外,還有一本帳:品質。量化對品質的影響,通常看困惑度(perplexity,PPL)的變化;ΔPPL 是量化後減 fp16 的差。

4-bit per-token 的 ΔPPL 如下(Cosmopedia,8192 個 held-out token):

模型FP16 PPL不旋轉(identity)SRHTSRFT
SmolLM2-135M6.61+1.96 ± 0.15+0.34 ± 0.01+0.38 ± 0.06
SmolLM2-360M4.87+0.97 ± 0.08+0.20 ± 0.03+0.21 ± 0.02
SmolLM2-1.7B3.77+1.04+0.16+0.16

(前兩列是 3 個 seed 的平均 ± 1σ;1.7B 因為計算成本只跑 1 個 seed。6-bit 與 8-bit 在這組測試裡無損。)

旋轉不是可選項。135M 不旋轉是 +1.96,旋轉後是 +0.38;差了大約 5 倍。這也解釋了為什麼「量化會壞」和「量化只是變慢」常常是同一件事的兩面:如果你的旋轉沒做對,4-bit 的誤差會直接反映在 PPL 上。

6-bit 和 8-bit 無損,4-bit 才是取捨點。3-bit 的絕對誤差更大,但這篇沒有把 3-bit 當賣點,我也不建議讀者從 3-bit 開始。

Qwen 的 4-bit 是另一個故事。Python reference 的 per-token 是 +7975.3,Metal 的 per-token 是 +7727.9;加上 per-channel λ 加 per-group g32 之後,融合版是 +638.6。這是 12.5 倍的改善,跟 Python reference 的 +654.6 同級。

但 +638.6 仍然是 +638.6。12.5 倍的改善,不等於可以上線。 論文把這個叫做「關掉災難」;我認為,它比較像是把火從整棟房子縮到一個房間,離安全還很遠。如果你要在 Qwen 上開 4-bit,請用任務指標驗收,不要只看 PPL 的相對改善。

Gemma 的結果比較好:per-token +18.7,融合後 +3.6。Python reference 是 −5.2,差距 +8.8 來自 kernel 的 fp32 reduction 與 rounding ordering,不是演算法。這一點作者自己做了兩個 ablation 排除。

它真的會壞:Qwen 2K 只吐兩個 token

現在回到那個「先別高興」。

Qwen2.5-1.5B 在 2048 前綴,int4 只生成 2 個 token 就 EOS;fp16 是 64 個。

項目fp16int4
生成 token 數(2048 前綴)642(EOS)
峰值記憶體4447 MB5527 MB
作者退路—約 1.5K 以上退回 fp16

原因不只一個。dequant-prefix cache 加上 int4 storage,讓峰值記憶體從 4447 拉到 5527 MB(+24%);再加上 28 層 full-attention 的量化噪聲累積,logits 被推去提前 EOS。

我認為,讓 1024 變快的那個設計,也是讓 2048 變壞的設計之一。prefix cache 把解量化成本從「前綴越長越慢」攤成固定成本,但它同時佔記憶體;在 Qwen 這種全注意力堆疊上,兩件事一起發生,就爆了。

Gemma 為什麼沒事?它的 sliding-window 層用 fp16,只有少數 full-attention 層付 dequant 成本;所以前綴拉長,成本成長得比較慢。架構不同,同一份量化 cache 的結果就不同。

如果你要在自己的模型上開這個 cache,先測 2K。不是測「能不能生成」,是測「生成出來的東西對不對、會不會提前停」。作者給的退路很明確:超過約 1.5K,就退回 fp16。

看 benchmark 要問的四件事

這篇論文最有用的地方,不是「Apple Silicon 比較快」,是它把一張 benchmark 拆成幾個要問的問題。

第一,平台。 這是 unified memory(CPU 與 GPU 共用記憶體)還是獨立 HBM(顯示卡自己的記憶體)?論文說:在獨立 HBM、dispatch 開銷高的 GPU 上,慣常的取捨會回復。也就是說,同樣的 kernel 搬過去,不一定還快。看到「量化加速」先問跑在哪。

第二,kernel。 這是 fused 還是四步分開?如果 benchmark 沒寫,數字可能被 dispatch 稅主導。你可以直接問:一層一步幾次 dispatch?

第三,前綴與架構。 256 前綴的 −8% 不是 4K 的 −3.2%;full-attention 堆疊和 sliding-window 的曲線也不一樣。要求對方給 256、1K、2K 三組數字,不要只看一張最好的圖。

第四,品質。 ΔPPL 的 baseline 與配方是什麼?+638.6 比 +7975 好,但仍然是 +638.6。per-token 和 per-channel+group 是兩種不同的東西,不能拿來互相比。

問題為什麼要問怎麼查
平台是 unified memory 還是獨立 HBM?取捨是否反轉的邊界看硬體規格;要求分平台數字
Kernel 是 fused 還是四步分開?相對 SRHT 可能慢 12–17%看 benchmark 是否寫「4 dispatches」或「fused」
前綴多長、模型是 full-attention 還是 sliding-window?長 context 可能失效要求 256/1K/2K 三組;看架構
ΔPPL 的 baseline 與量化配方是什麼?+638.6 比 +7975 好,但仍是 +638.6看 baseline、per-token 或 per-channel+group

別過度解讀

  • 單一平台:只有 Apple M1;獨立 HBM 的 GPU 沒測。
  • 模型都是小模型(135M–1.7B),沒有 7B 以上。
  • 單人預印本,未同儕審查;我們沒有獨立複現。
  • Gemma 的 19.5× 不是同層比較;full-attention 層內約 3.2×。
  • Qwen 2K 會壞;作者自己寫了 1.5K 的退路。
  • 這篇不是說「融合是唯一解」;它只處理四步拆開這一種成本。

結論:問題不是「量化快不快」

量化不是一個開關,是一條工程鏈:平台、kernel、量化配方、前綴長度、模型架構,任何一個換了,答案就換了。

這篇的兩本帳,可以這樣記:

  • 實作稅 是同一份方法,eager 四步對融合兩顆,差距 11–21 個百分點。
  • 部署帳 是 int4 cache 對 fp16,短前綴最多 −8.2%,長前綴會縮到個位數百分比(Qwen 1024 是 −1.6%,Gemma 4096 是 −3.2%),而且可能提前 EOS。

明天可以先做一件事:用你的模型跑 256、1K、2K 三個前綴,記錄 ms/tok 和輸出品質;如果模型是 full-attention 堆疊,1.5K 以上先留 fp16 退路;2K 是測試點。

如果你要的是有人陪你把自己的模型跑過這三個前綴,我們的企業 AI 導入健檢做的就是這件事。先判斷哪些工作現在就能交給 AI,哪些還不成熟。

量化系列走到這裡,三篇的弧線是:strata 講取捨,broken-on-arrival 講配方,這篇講實作。同一個問題,從「要不要量化」走到「怎麼寫那個 kernel」。


論文:arXiv 2605.05699v1(Mohamed Amine Bergach, Illumina)。前作:125B 模型跑在 12GB 顯卡、你下載的量化模型可能是壞的。