~/blog/gtx-970-gemma4-e2b-quantization-benchmark

LLM Deep Dive · part 4

[趣味競賽] GTX 970 跑 Gemma 4 E2B:最大的量化檔反而最快(47.6 tok/s)

cat --toc

TL;DR

我把 Google 的 Gemma 4 E2B 塞進一張 2014 年的 GTX 970——可用 VRAM 只有 3.5GB、沒有 tensor core——四種量化跑同樣六項測試。結果跟一般人對新卡的印象完全相反:最大的檔案反而最快。Google 3.2GB 的 QAT Q4_0 跑 47.6 tok/s;bartowski 2.9GB 的 Q2_K,檔案最小,只有 32.8 tok/s。在沒有 tensor core 的卡上,推理卡在解量化的計算,不是記憶體頻寬,所以最簡單的格式贏,就算它要讀更多位元組。四種量化全部通過英文、中文問答、單次與平行 tool call、JSON、程式碼生成。每項只跑一次——這是 smoke test,不是論文。

白話版:老卡跑新模型,反常的地方在哪

2026 年了,誰還會拿 GTX 970 跑 AI?大概沒人。但正是這個「沒人」最好玩:一張 2014 年、二手幾乎沒人要的老卡,居然跑得動 Google 今年的 Gemma 4 E2B——還能用英文中文回答、會呼叫工具、會寫程式。

最好玩的是一個反常結果:大家都推薦的「比較聰明」的壓縮方式,在這張老卡上居然比最笨的那種還慢。原因是老卡跟新卡的瓶頸不一樣。新卡卡在「搬資料」,所以檔案越小越快;這張老卡卡在「把壓縮的權重解開來算」,所以解得最簡單的格式反而最快,就算檔案比較大。

這篇就是在講:為什麼同一個問題,換一張卡,答案會整個翻過來。


前言

前陣子我把一個 122B 塞進 128GB 的 GB10,看它在小盒子上爬。這篇反過來——把一個 5.1B 的模型塞進一張 3.5GB 的 12 年老卡,看它跑多快。一個是貴森森的盒子硬撐超大模型,一個是快被當電子垃圾的顯卡跑剛剛好的模型,聽起來八竿子打不著,其實是同一件事。

這是 LLM Deep Dive 第四篇。Part 1 講了各種量化演算法在做什麼,這篇就是拿四種量化在最極端的硬體上對打,看那些演算法的差異在老卡上會放大成什麼樣子。

一張 2014 的卡,一個 2026 的模型

GTX 970 是 2014 年 9 月的卡。Maxwell 架構,compute capability 5.2,沒有 tensor core(那要等到 2017 年的 Volta),還有那個惡名昭彰的「3.5GB + 0.5GB」設計——名義上有 4GB,但最後 0.5GB 接在慢速的記憶體上,當年被嫌到爆,實際規劃時就當它只有 3.5GB。

模型是 Google 的 Gemma 4 E2B。「E」是 effective(有效)的意思:有效參數 2.3B,但加上 embedding 總共 5.1B——這來自 Per-Layer Embeddings(PLE),一堆住在權重裡、但幾乎不吃計算的查表。它還帶一個 262,144 個 token 的詞彙表。這個大詞彙表就是為什麼一個「2B」模型量化後還是 3GB 起跳,而且它在載入時還會卡你一下(後面講)。模型號稱 128K context、思考模式、原生 tool use、還有視覺——970 一個都用不到,但正是這種為了在裝置上跑而做的設計,才讓它塞得進去。

我把 llama.cpp 的 CUDA backend 編成 Maxwell 版,llama-server 全部丟 GPU:

cmake .. -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=52   # 5.2 = GTX 970
./build/bin/llama-server \
  -m ~/models/gemma-4-E2B-QAT-Q4_0.gguf \
  -ngl 99 -c 2048 --port 8080 \
  --jinja --chat-template-kwargs '{"enable_thinking":false}'

四種量化,都小到能整個載進 VRAM:

模型類型檔案大小來源
gemma-4-E2B-it-Q2_Kimatrix PTQ Q2_K2.9 GBbartowski
gemma-4-E2B-it-IQ3_Mimatrix PTQ IQ3_M3.0 GBbartowski
gemma-4-E2B-it-Q3_K_Mimatrix PTQ Q3_K_M3.1 GBbartowski
gemma-4-E2B-QAT-Q4_0QAT Q4_03.2 GBGoogle 官方

先釐清一個常見誤解:bartowski 的 imatrix 量化不是「暴力」量化。真正的暴力是 RTN(直接四捨五入、沒有校準)。正確的對比是 QAT(訓練感知)vs imatrix-PTQ(校準感知)vs RTN(完全沒校準)。這篇比的是前兩個。

反直覺的地方:最大的檔案最快

同樣六項測試,temperature 0.1,每項跑一次:

測試Q2_K (2.9G)Q3_K_M (3.1G)IQ3_M (3.0G)QAT Q4_0 (3.2G)
英文問答32.529.130.044.7
中文問答31.929.230.347.0
Tool use(單次)33.830.531.950.3
Tool use(平行)32.829.531.247.6
JSON 輸出32.829.430.549.1
程式碼生成32.829.930.346.9
平均32.829.630.747.6
QAT Q4_0  ████████████████████████████████████████████████  47.6 tok/s  (+45%)
Q2_K      █████████████████████████████████                 32.8 tok/s  (基準)
IQ3_M     ███████████████████████████████                   30.7 tok/s  (-6%)
Q3_K_M    ██████████████████████████████                    29.6 tok/s  (-10%)

把這個排名跟檔案大小擺一起看。3.2GB 的 QAT Q4_0——最大的檔——比 2.9GB 的 Q2_K 快 45%。在新卡上你幾乎不會看到這種事:GB10 或 5090 卡在記憶體頻寬,每個 token 搬的位元組越少、吐得越快,最小的量化通常會贏。這裡順序整個顛倒。真正決定速度的不是頻寬,而是別的瓶頸。

為什麼沒有 tensor core 的 Maxwell 卡卡在解量化

每種量化格式都得先把壓縮後的權重轉回運算單元能處理的數值。要解多少,跟格式關係很大:

  • Q4_0——固定區塊裡的平 4-bit 整數,一個區塊一個 scale。解碼幾乎沒成本:讀一個 nibble,乘上 scale。
  • K-quant(Q2_K、Q3_K_M)——把區塊再切成子塊、各自帶量化過的 scale(Q2_K 還多帶每個子塊的 min,Q3_K 沒有)。每個權重要解的更多。
  • I-quant(IQ3_M)——runtime 靠固定的 codebook/grid 查表解碼,而 importance matrix 是在量化時拿來決定精度怎麼分配的。三個裡 runtime 解得最多。

在有 tensor core 的新卡上,這些都不重要,因為瓶頸是從 VRAM 讀位元組、不是解它。在 GTX 970 上沒有 tensor core、整數吞吐又弱,所以解量化的運算就是每個 token 的主要成本。解得最省的贏,那就是 Q4_0——就算它檔案最大。那些聰明格式省下來的位元組,全花在 970 不擅長的解碼運算上,還倒貼。

所以「最佳量化」這個問題沒有通用答案。它跟著硬體變。(這些量化演算法本身怎麼運作,看 Part 1:量化演算法到底在做什麼。)

QAT 沒讓它變快——是 Q4_0 讓它快的

很容易寫成「Google 的 QAT 模型贏了」。其實沒有,至少速度上沒有。QAT——量化感知訓練——是在訓練時調權重的數值,讓模型更耐低精度。它改的是數字本身,不是 kernel 怎麼讀。推理速度來自格式,而這裡的格式是 Q4_0。

同一個模型做一個普通的事後量化 Q4_0,速度會差不多。QAT 改善的是品質、不是吞吐量;要比品質得另外測,那才是它的價值。把速度算在 QAT 頭上很直覺,但其實是錯的;速度是格式的功勞。

品質:四個都過,只有一項拉開差距

速度只講了一半。品質上四種量化每一項都通過:

  • 英文、中文問答——全對、全流暢。Q2_K 的繁體中文乾淨,沒有簡體字漏出來。
  • Tool use,單次跟平行——四個都吐對的呼叫,問「比較台北跟東京」時四個都正確發出兩個 tool_calls
[
  {"name": "get_weather", "arguments": {"city": "Taipei"}},
  {"name": "get_weather", "arguments": {"city": "Tokyo"}}
]
  • JSON 輸出——全部合法,Year 都正確用數字型別、不是字串。

但這裡有個陷阱,跟我上週重建一個 tool-call 流程踩到的是同一個:「過」是很低的標準。每個量化都「通過」tool use,是因為那個 prompt 只是查單一城市的小事——它量的是「會不會吐合法 JSON」,不是「在難一點的情況下對不對」。真正把四個拉開的是程式碼生成。叫它寫一個 palindrome 檢查,三個都寫了教科書的一行:

return s == s[::-1]

只有 IQ3_M 先把輸入正規化:

cleaned_s = ''.join(char.lower() for char in s if char.isalnum())
return cleaned_s == cleaned_s[::-1]

這個版本才處理得了 "A man, a plan, a canal: Panama"——其他三個默默漏掉的 edge case。這應該是 importance matrix 在做事:把精度留在最影響推理的那些權重上,讓模型還有餘力去想那個難的 case。值得記一下,但別過度解讀——這只是一個 prompt、跑一次。算是個訊號,不是定論。

收穫

最花時間的地方

那個 262K 詞彙表。它不只讓檔案變大,也明顯拉長啟動時間。llama-cli 每次查詢都重載一次那個 tokenizer——一次大概三分鐘。解法是 llama-server 開一次,之後打它的 OpenAI 相容 API。讓 2B 模型重到 3GB 的大詞彙表,也是讓你隨手一設就覺得壞掉的同一個東西。

可以重複用的判斷方法

「大檔反而快」是個有用的線索,不是奇聞。它代表你卡在解量化的計算、不是記憶體頻寬,也就代表你在老的、或整數運算弱的硬體上。可以抓一個原則:有 tensor core 的卡,在 VRAM 塞得下的前提下選檔案最小的量化(卡頻寬);沒有 tensor core 的卡則選解量化最簡單的格式,例如 Q4_0(卡計算)。同一個模型,相反的選擇。

通用原則

沒有通用的量化排名。我這篇回答的那個 Q2_K-vs-Q4_0,在 GTX 970 跟 GB10 之間會翻過來。在你手上那張卡上測——理論會給你一張排版乾淨、結論篤定的對照表,然後騙你。

結論:實際該跑什麼

  • 沒有 tensor core 的卡(GTX 9xx/10xx 那輩):QAT Q4_0,或任何 Q4_0,衝速度。
  • 常跑吃邏輯推理、又在意輸出品質的任務:IQ3_M——imatrix 帶來的品質差異值得那一點效能成本。
  • 有那個 262K 詞彙表,一定 llama-server、不要 llama-cli
  • 編 llama.cpp 給 970 加 -DCMAKE_CUDA_ARCHITECTURES=52commit 42a0afd59,2026 年 6 月)。

我跑這個的同一週,手上還有一個 122B 在 128GB 的 GB10 上爬。機架的一頭:frontier 等級的模型擠在小盒子裡。另一頭:5.1B 模型在一張 12 年老卡、3.5GB 上跑。兩邊其實是同一件事——最後還是硬體決定效能,唯一準的辦法就是直接在那台機器上測。

這台機器的 GPU 在二手市場大概值一頓飯錢,但它跑得動 2026 年的模型、會用工具、會寫 code。垃圾佬的浪漫大概就是這個:不是每個人都需要 5090,有時候一張老卡就夠你入門了。


附錄:WSL2 筆記

970 插在一台 Windows 11 機器(Ryzen 5 3500X、16GB),我從 WSL2 進去。幾個雷:

  • WSL2 裡 nvidia-smi 不在 PATH 上——在 /usr/lib/wsl/lib/nvidia-smi
  • GPU 使用率在 WSL2 裡推理中也顯示 0%。別信它,信 tok/s。
  • 3500X 沒有內顯,所以拔掉 HDMI 或關掉遠端桌面,Windows 會以為機器沒螢幕、可能把 GPU 收掉。留一個螢幕接著。
  • WSL2 編譯小技巧:-j4,不要 -j32——預設的記憶體上限會把編譯 OOM 掉。.wslconfigmemory=8GB

常見問題

GTX 970 跑得動 Gemma 4 E2B 嗎?
跑得動。我測的四種量化(Q2_K、Q3_K_M、IQ3_M、QAT Q4_0)都塞得進 970 約 3.5GB 的可用 VRAM,用 llama.cpp 全 GPU offload,速度約 29–48 tok/s。
為什麼比較大的 QAT Q4_0 在 GTX 970 上反而比較小的 Q2_K 快?
970 沒有 tensor core、整數運算又弱,所以每個 token 的成本卡在解量化的計算,不是讀記憶體。Q4_0 是平的 4-bit 區塊,解碼最省;K-quant 的子塊、I-quant 的查表都更吃力。所以 Q4_0 檔案最大還是最快。
QAT 會讓推理變快嗎?
不會。QAT 只改權重的數值來救品質,不改推理 kernel。這裡速度贏的是 Q4_0 這個格式,不是 QAT。一個普通的事後量化 Q4_0 速度會差不多。