~/blog/fp16-vs-q8-rtx5090-2026

LLM Deep Dive · part 7

[Benchmark] FP16、Q8 怎麼選?RTX 5090 跑 Qwen2.5-7B,Q8 每秒 188.75 token

2026-11-119 分鐘閱讀#fp16-vs-q8#量化#q8_0#q6_kEnglish
❯ cat --toc

TL;DR

同一張 RTX 5090 跑 Qwen2.5-7B,Q8_0 吐字每秒 188.75 token,FP16 是 109.67,約 1.72 倍。Q8 全卡 VRAM 取樣峰值中位數是 10,467 MiB,含桌面本身佔用。這個模型我會先選 Q8_0,要省記憶體再往 Q6_K、Q4_K_M 選。讀題速度和實際答題品質要另外看。

同一張 RTX 5090 比較 FP16、Q8_0、Q6_K 與 Q4_K_M

前言

下載模型時,同一個名字後面掛著 FP16、Q8_0、Q6_K、Q4_K_M。容量差很多,那速度跟品質呢?我想知道的是:卡上明明放得下 FP16,改抓 Q8 到底划不划算。

這次我拿一份 Qwen2.5-7B-Instruct 的 FP16 GGUF,自己量化出另外三份,全部在同一張 RTX 5090 跑。還不熟這些名字,可以先看量化入門;這篇直接拿實測幫你選。

Q8 吐字更快,讀題則是 FP16 較快

先分清楚兩種速度。讀題是模型把你的輸入讀進去,吐字是開始回答後,一段一段往外產生文字。表裡的 token 是模型處理文字時切出的小片段;tok/s 越大,表示每秒處理得越多。

看這張表時,讀題跟吐字分開比。速度取三次中位數;VRAM 則是每次執行時,整張卡取樣峰值的中位數,包含 Windows 桌面及其他桌面程式。

格式讀題 tok/s吐字 tok/s全卡 VRAM 取樣峰值(MiB)
FP1616,447.2109.6716,877
Q8_015,953.6188.7510,467
Q6_K13,175.0231.498,835
Q4_K_M15,407.2284.717,471

我最在意的吐字,Q8_0 是 FP16 的 1.72 倍;繼續往下壓到 Q4_K_M,吐字來到每秒 284.71 token。讀題的排序卻是 FP16、Q8_0、Q4_K_M、Q6_K。檔案比較小,沒有讓每個步驟都跟著變快。

四種格式的讀題與吐字速度分開比較
三次實測的中位數。左右刻度不同,四種格式使用相同參數。

權重縮小後,需要搬的資料變少,這可能有助於吐字速度。不過這次沒有拆開量每個 GPU 運算步驟,不能把所有差異都歸因於搬資料。Q6_K 讀題比較慢,我量到了;究竟卡在哪個步驟,這次還沒查。

Q8 的預測分布最接近 FP16,Q4 的差距較大

光看速度還不夠,我也拿同一份英文文章測下一個 token 的預測。這裡有兩個指標:perplexity 看模型猜原文下一個 token 猜得好不好,用這份文字測試時,數字越低越好;KL divergence 看預測機率分布跟 FP16 差多少,越接近零就越像。兩個都不是「答題正確率」。

語料固定為 Wikitext-2 test 的前 32 個區塊,每個區塊用 512-token context。表中的 perplexity 差值,對照直接跑 FP16 的結果;KL 則對照存下來的 FP16 預測資料。

格式Perplexity與直接 FP16 的差KL 對儲存的 FP16 logits
FP166.098206+0.000000-0.000010
Q8_06.106598+0.0083920.001709
Q6_K6.094548-0.0036580.004940
Q4_K_M6.308980+0.2107740.039512

量化的三份裡,Q8_0 的 KL 最小。Q6_K 的 perplexity 甚至略低於 FP16,但我不會因此說它比較聰明:猜中原文的下一個 token,跟整份機率分布像不像 FP16,是不同的問題;這份樣本也不長。

FP16 跟自己的儲存資料相比,KL 跑出 −0.000010。真正的 KL 不會是負數,這是接近零的數值估計誤差。儲存資料也有精度損失:工具重建參照時列出的 perplexity 是 6.085444,直接跑 FP16 則是 6.098206。所以我保留原始值,沒有把負數改成零,也沒有混用這兩個 perplexity 基準。llama.cpp 的說明有交代儲存 logits 的格式;logits 就是換算預測機率前的分數。

這個模型先選 Q8,要省容量再往下選

放得下就先用 Q8_0,要再省 VRAM 用 Q6_K,把容量或吐字速度排最前面就試 Q4_K_M;FP16 留給對照實驗,或特別在意讀題速度的工作。

Q8_0 在這次測試裡,吐字比 FP16 快,預測分布也最接近原版。Q6_K 又省一些 VRAM、吐字再快一些,但分布偏移比較多。Q4_K_M 的檔案最小、吐字最快,perplexity 跟 KL 的變化也比較明顯,拿來工作前先用自己的題目試。

這次沒有量長上下文、多人同時使用、中文 perplexity,也沒有跑程式或數學答題。英文樣本的 KL 很小,還不能替這些工作掛保證。

進階:參數、原始紀錄與重跑方式

不讀這節不影響選格式;下面留給想自己重跑、核對數字的人。

三種量化都從同一份 FP16 產生

來源是 bartowski 的 FP16 GGUF,版本固定在這個 revision。下載後先核對檔案的 SHA256,確認與 HF 的 LFS 物件所列的值相同,再用同一個 llama-quantize 產生三份檔案,沒有套 importance matrix,也就是沒有另外用樣本調整量化時各權重的重要程度。這樣不會混進不同製作者的量化做法。

以下是實際命令。執行目錄裡有 model-f16.gguf,llama.cpp 執行檔放在 bin。輪到別的格式時,測速命令只換 -m 後面的檔名。

.\bin\llama-quantize.exe model-f16.gguf model-Q8_0.gguf Q8_0 8
.\bin\llama-quantize.exe model-f16.gguf model-Q6_K.gguf Q6_K 8
.\bin\llama-quantize.exe model-f16.gguf model-Q4_K_M.gguf Q4_K_M 8

.\bin\llama-bench.exe -m model-f16.gguf -p 512 -n 128 -d 512 -b 512 -ub 512 -t 8 -ngl 99 -fa on -ctk f16 -ctv f16 -r 1 -o json
項目本次條件
日期2026-11-11,Asia/Taipei
GPU/驅動RTX 5090,Windows WDDM,NVIDIA 621.14
CPU/系統Ryzen 9 9950X;Windows 11 build 26300
llama.cppb12250,commit ae4893a092e060cfbd4979600bf3c1e48a710874,Windows CUDA 13.5 發行檔
模型參數量7,615,616,512,來自 llama-bench 輸出
工作量單流、合成 token;prompt 512、generation 128、depth 512
Batch/microbatch/CPU threads512/512/8
GPU/attention/KV-ngl 99、Flash Attention on、K 與 V 都用 F16
重複方式四輪,整輪丟棄第 0 輪;第 1–3 輪取中位數與範圍;每次命令 -r 1
其他預設內建暖身開啟;load/lazy mode auto、repack on、不啟用 fit target;其他值留在完整 JSON

四輪依序正向、反向、正向、反向跑。所有速度欄位裡,最大與最小值差距最多是中位數的 1.32%,沒有超過實驗前訂的 15% 補量門檻。這裡報中位數與範圍,沒有宣稱做過顯著性檢定。

每次都另開測試行程,不經常駐模型服務,所以沒有 serving prefix cache。四種格式也沒有更換執行檔版本,不需要清掉其他服務的編譯快取。每次測試開始前都確認 ComfyUI 佇列空白;若出現其他 compute 行程,或閒置時的 GPU 使用率與記憶體高於門檻,腳本先等。原本的桌面程式保留,這是 Windows 桌面環境的數字。這套測速不包含文字切成 token、挑選輸出 token 的時間,也不等於聊天軟體的完整回應速度。

VRAM 看整卡取樣,檔案大小另外列

測試行程執行時,大約每 0.2 秒用 nvidia-smi 取樣一次。實測前的全卡用量是 2,379–2,403 MiB。WDDM 沒有提供可用的每行程記憶體數字,因此我沒有把全卡用量當成「模型本身佔多少」;取樣也可能漏掉更短的尖峰。

這張表保留三次的最小值、最大值,以及磁碟上的精確 bytes。

格式讀題 min–max tok/s吐字 min–max tok/s全卡峰值 min–max MiB檔案 bytes
FP1616354.69–16553.35109.604–109.69916862–1688715237853600
Q8_015950.65–15963.68188.614–188.75610442–104678098525600
Q6_K13028.98–13203.40231.384–231.7168810–88356254199200
Q4_K_M15323.84–15462.58284.697–284.7447410–74714683073952

每輪原始數字包含被丟棄的暖身輪;summary.json保留中位數與範圍使用的陣列。同一個目錄也有每次的 stdout 與 VRAM 取樣,例如 bench-r1-Q8_0.stdout、bench-r1-Q8_0.vram.json。

品質測試共用同一份 FP16 參照

.\bin\llama-perplexity.exe -m model-f16.gguf -f wikitext-2-raw/wiki.test.raw -c 512 --chunks 32 -b 512 -ub 512 -t 8 -ngl 99 -fa on -ctk f16 -ctv f16 --kl-divergence-base fp16-reference.kld

.\bin\llama-perplexity.exe -m model-Q8_0.gguf -f wikitext-2-raw/wiki.test.raw -c 512 --chunks 32 -b 512 -ub 512 -t 8 -ngl 99 -fa on -ctk f16 -ctv f16 --kl-divergence-base fp16-reference.kld --kl-divergence

第二道命令再依序把模型換成 FP16、Q6_K、Q4_K_M。各次都用前 32 個區塊,不是完整 Wikitext-2 test。原始語料 SHA256 是 173c87a53759e0201f33e0ccf978e510c2042d7f2cb78229d9a50d79b9e7dd08。儲存的參照檔是 2,481,815,316 bytes,SHA256 是 3c765b5a27dc4dca897d628d3fb3b2c1b386b36243071424345872cf0e1d88f0。

品質測試的 log 留著 tokenizer 警告:一個長得像控制符號的 token 被標成其他類型,被 loader 自動覆寫。四種格式繼承同一份 FP16 來源的 tokenizer metadata,並使用同一個 loader,我沒有另外改 tokenizer。

FP16、Q8_0、Q6_K、Q4_K_M的原始輸出都有逐區塊結果、不確定度與 KL 統計。品質計算的不確定度,跟測速重跑三次的範圍,是兩種不同的東西。

用 SHA256 對回每一份模型

FP16: 863c978275bca3fdacdde06cbfc1a65f2bd65210bb7ecfd9ded3b24219d81b54
Q8_0: 6925c96c0e9a6af97a81521caf2dd58da9f0fc5f00620c4d131684b872a6908e
Q6_K: 9616f5a674b8d53dd2c8db918e4d8b5f331a4cca342ba2040c47371a73faecf9
Q4_K_M: 246ac71c280bd514575413b01abef9954f9148c011e4a06df643001a0a6b1910

完整 manifest另列語料、參照與執行檔的 hash。Windows 的小型 exe 還會載入實作 DLL,所以不能單靠 launcher 的 hash 認定整套執行檔相同;固定發行版本與 CUDA backend hash 也要一起留。

常見問題

RTX 5090 跑 Q8 會比 FP16 快嗎?
這次 Qwen2.5-7B 單流測試,Q8_0 吐字每秒 188.75 token,FP16 是 109.67;讀題則是 FP16 較快。兩者用同一版 llama.cpp、固定 512-token depth,不能直接推廣到其他模型或長上下文。
Q8_0、Q6_K、Q4_K_M 該下載哪個?
這個模型我會先選 Q8_0;要再省 VRAM 就用 Q6_K,優先考慮容量或吐字速度則試 Q4_K_M。Q8_0 在這份英文樣本的輸出分布最接近 FP16,但這不代表它在你的工作上一定答得一樣好。

接著讀

不想錯過新文章?

訂閱我確保不漏接!

隨時一鍵退訂。