iT邦幫忙

2026 iThome 鐵人賽

DAY 8
0
AI Engineering

不是模型太慢,是你沒算過這筆帳:地端 LLM 工程實戰 30 天系列 第 8

Day 08 - 一大票顯卡對比文都算錯了:M4 Max 對 4070 Ti、5090 與 RTX PRO 6000

  • 分享至 

  • xImage
  •  

昨天結尾說「萬事俱備」。W1 磨的尺、認的模型、摸清的記憶體線,今天全部上場:同一顆模型,MacBook 與 4070 Ti 誰快?

動手之前你八成會先 Google,然後找到兩張躺在 llama.cpp 官方 repo 裡、看起來再權威不過的表。各抄一行、相除、寫結論,網路上不少對比文就是這樣生出來的。

先講結論:**這兩張表各自都是好資料,放在一起直比卻是錯的。**上半場拿它們當反面教材,下半場把正確的對照搭給你。

兩張官方表,先貼再拆

先解釋兩個代號。pp512 是把 512 個 token 的 prompt 一口氣讀完,就是 Day 02 的 prefill;tg128 是另外量生成 128 個 token,就是 decode。兩者獨立,tg128 不接在那 512 個之後。

第一張,llama.cpp 作者彙整的 Apple Silicon 效能串:

機器 backend 量化 權重(GB) tg128(t/s) 權重讀取等效(GB/s) 佔帳面 546
M4 Max 40-core GPU Metal Q4_0 3.83 83.06 318 58%
M4 Max 40-core GPU Metal Q8_0 7.16 54.05 387 71%
M4 Max 40-core GPU Metal F16 13.48 31.64 427 78%

註:取自 llama.cpp discussion #4167,社群以統一指令回報、作者彙整;全串鎖 git checkout 8e672efe(2023-11)。模型為 Llama 2 7B。Q4_0 的 pp512 是 885.7。

把「權重大小 × tg」當成每秒讀過的權重量(實際流量還有 KV 與 activation,kernel 裡也有 cache 與解量化行為,所以只是等效值)。同機同 backend、只換量化,這個等效值就從帳面 546 的 58% 走到 78%——換個量化差二十個百分點。它不是常數,是一條帶,Day 10 整篇在講。

第二張,同一個 repo 裡由 collaborator 維護的 Vulkan 效能串:

機器 帳面頻寬 pp512(t/s) tg128(t/s) commit 日期
RTX 5090 1792 GB/s 10381.64 ± 508.84 263.63 ± 0.91 2025-10-05
RTX 4090 1008 GB/s 9452.03 ± 187.70 187.97 ± 0.21 2025-09-24
RTX 3090 936 GB/s 4666.15 ± 12.89 164.05 ± 0.89 2026-05-02
RTX 4070 Ti Super 672 GB/s 6099.18 ± 154.30 129.45 ± 0.18 2025-09-24
RTX 4070 Ti 504 GB/s 4981.44 ± 102.35 110.53 ± 0.00 2026-01-14

註:取自 llama.cpp discussion #10879 的 no FA 表,多人回報,全部 Llama 2 7B Q4_0、Vulkan。帳面頻寬欄不在原表上,是照 Gbps × bit ÷ 8 換算的。同串另有 FA enabled 表,這五張卡的 tg 約高 1~5%。

FA 一開一關就差上幾個百分點,這是第六根釘子的由來。而這張表最值得看的是最後一欄:五列同串同模型同 backend,看起來整齊,commit 卻橫跨 220 天

疊起來看:pp 4981 對 885.7,4070 Ti 快 5.62 倍;tg 110.53 對 83.06,只差 1.33 倍。勝負已分?慢著。這個除法本身就是錯的。

三個變因,一個都沒控制

https://ithelp.ithome.com.tw/upload/images/20260819/20183550iaf1sNR2l6.png
圖 1:兩張官方表各自都對,放在一起就錯。沒控的是 backend、source revision 與回報來源;模型反而是兩串刻意對齊過的。

**變因一:backend 不同。**Mac 跑 Metal,4070 Ti 跑 Vulkan,而 Vulkan 甚至不是 N 卡的主場。這正是 Day 02 那張表永遠帶 backend 欄的原因。

變因二:source revision 不同。先幫兩張表平反:模型其實是同一顆 Llama 2 7B Q4_0,#10879 開宗明義說要跟前一串保持一致,就是為了能對照。

沒控的是版本。#4167 全串鎖死在 2023 年的 commit,#10879 讓每個人用當下的版本。差多少?串主自己留了對照:同一台 M2 Ultra 隔兩年重跑,pp 與 tg 各高一成半。硬體一顆螺絲沒動,數字自己會走。

**變因三:回報來源不受控。**兩串都是各路網友用自己的機器回報,彼此沒有協調。你等於拿 A 群陌生人的成績,去除 B 群陌生人的成績。

各自都對,直比就錯。網路上大量「M4 Max vs 某卡」的文章犯的正是這個錯。先問這些數字是不是同一場比賽跑出來的。

但數字的形狀還是洩了題

不可直比,不代表沒有資訊,比例的「形狀」會說話。

https://ithelp.ithome.com.tw/upload/images/20260819/20183550CxkHU1ZgwR.png
圖 2:pp 的差距遠大於 tg,符合 compute-heavy 與 bandwidth-heavy 的形狀。但帳面說 Mac 該小勝 1.08 倍,兩張表卻說 4070 Ti 快——缺口是 Day 10 要算的東西。

Day 02 埋的那句今天發芽:prefill 較吃運算吞吐,decode 較容易受頻寬限制。pp 拉開 5.62 倍、tg 只拉開 1.33 倍,形狀符合預期;但 backend 與 revision 都不同,倍率不能當算力或頻寬的量測

同表內部還有一條線索:從 4070 Ti 到 5090,帳面頻寬增加 3.56 倍,tg 只增加 2.39 倍——tg 沒有跟帳面頻寬線性縮放。缺口是 kernel 效率、固定成本還是版本差異,這張跨 revision 的表回答不了。

正確的對照怎麼搭:六根釘子

別幻想只剩「硬體」一個變因:跨 Apple Silicon 與 NVIDIA,backend、OS、driver 本來就做不成一樣。該釘死的是模型、source revision、workload、FA、GPU 數量與測試流程;平台整包當一個變因,M4 Max 加 Metal 對 4070 Ti 加 CUDA。這張表回答的是兩套平台各出主場後端誰快。

https://ithelp.ithome.com.tw/upload/images/20260819/20183550THS1QE4llj.png
圖 3:六根釘子釘死可控制項,剩下的是整包平台變因——M4 Max 加 Metal 對 4070 Ti 加 CUDA。backend 欄照抄工具輸出。

  1. 同一個 GGUF 檔:兩台各算一次 SHA256。我這次就踩到,ollama pull 的 blob 跟 HF 上同名的 Q4_K_M 根本是兩個不同的檔。
  2. 同一個 source revision:兩台鎖同一個 tag。我這組兩邊都用官方預編譯包,最乾淨;自己編的話 compiler 與 toolkit 是另一套,要記在表上。
  3. 同參數-p 512 -n 128,重複 10 次。
  4. 單卡紀律:多張卡就用 CUDA_VISIBLE_DEVICES 鎖死,別讓引擎自己決定切不切。
  5. backend 欄照抄輸出:工具印 MTL,BLAS 就寫 MTL,BLAS,不要潤色成「Metal」。
  6. flash attention 顯式寫死-fa 預設是 auto,而吃 auto 跑出來的表根本不印 fa 欄——不寫死就沒有紀錄。

換成 MLX 或 TensorRT-LLM,這張表就不保證還成立。

模型從 Day 06 六組裡挑。Qwen3.6-27B 的 Q4_K_M 要 16.8GB,12GB 放不下,容量線在 Day 08 就先咬人。唯一留得下舒服餘裕的是 Gemma 4 12B 官方 QAT,6.48 GiB;gpt-oss-20b 字面上也「塞得進」,但幾乎不剩空間,那正是明天的主題。

T1 — macOS / zsh

TAG=b10488
GGUF=$PWD/gemma-4-12b-it-qat-q4_0.gguf     # 絕對路徑,下面會 cd 進別的目錄
shasum -a 256 "$GGUF"                       # 兩台對指紋,一個字元都不能差

curl -LO https://github.com/ggml-org/llama.cpp/releases/download/$TAG/llama-$TAG-bin-macos-arm64.tar.gz
tar -xzf llama-$TAG-bin-macos-arm64.tar.gz && cd llama-$TAG
xattr -dr com.apple.quarantine .            # 官方 binary 未經公證,Gatekeeper 擋住時對過指紋再移除
./llama-bench -m "$GGUF" -p 512 -n 128 -r 10 -fa off -ngl 99 -o md

T2 — Windows / PowerShell(官方有 CUDA 預編譯包,但要抓兩個 zip:binary 一個、CUDA runtime DLL 一個)

$TAG  = "b10488"
$GGUF = "$PWD\gemma-4-12b-it-qat-q4_0.gguf"
(Get-FileHash -Algorithm SHA256 $GGUF).Hash     # 跟 macOS 那邊比對

$base = "https://github.com/ggml-org/llama.cpp/releases/download/$TAG"
curl.exe -LO "$base/llama-$TAG-bin-win-cuda-13.3-x64.zip"
curl.exe -LO "$base/cudart-llama-bin-win-cuda-13.3-x64.zip"
Expand-Archive "llama-$TAG-bin-win-cuda-13.3-x64.zip" -DestinationPath llama-$TAG
Expand-Archive "cudart-llama-bin-win-cuda-13.3-x64.zip" -DestinationPath llama-$TAG -Force
cd llama-$TAG

nvidia-smi --query-gpu=driver_version,power.limit --format=csv,noheader   # 記到表上
.\llama-bench.exe --list-devices                # 先看清楚有幾張、編號是什麼
$env:CUDA_VISIBLE_DEVICES = "0"                 # 多張卡就鎖死一張
.\llama-bench.exe -m $GGUF -p 512 -n 128 -r 10 -fa off -ngl 99 -o md

Linux 沒有官方 CUDA 包,得自己編:cmake -B build -DGGML_CUDA=ON && cmake --build build -j$(nproc)

-p 512-n 128 剛好等於現行預設;-r 10-ngl 99-fa off 是刻意指定——-r 預設只有 5、-ngl 是 -1、-fa 是 auto。重點不是追著預設跑,是半年後還能重跑同一場比賽。

表怎麼讀:± 那一欄才是主角

llama-bench 吐出來的是 551.90 ± 29.53,那個 ± 是同一個 process 內重複測試的標準差,別在貼進文章時刪掉——一個沒有 ± 的數字,你分不出它是結論還是雜訊

https://ithelp.ithome.com.tw/upload/images/20260819/20183550TdJVD9EMpm.png
圖 4:同一顆模型、同一組指令,T1 上跑序能造出 16% 的假差距,T2 上只剩 0.6% 的雜訊。同一個方法論結論,換台機器就未必成立。

示範一次踩坑。跑 -fa off,on 拿到 551.9 對 462.6,像是「開 FA 慢 16%」;順序倒過來跑 -fa on,off,變成 534.7 對 462.7——「FA 快 16%」,結論相反。而排第二的兩次是 462.64 與 462.69,差 0.01%,完全不管 fa 設成什麼。慢的不是 FA,是跑第二輪的那一組。

但這句話有範圍。同一組對照搬到 T2 那台 Windows 桌機,跑序效應完全消失:fa=off 排第一或第二都是 3805-3828,fa=on 都是 4164-4181。而在 T2 這台、這組 workload 上,FA 的效果是真的:pp 約 +9%。

它很像熱與功耗造成的,但我沒同步記錄溫度與功耗,又同時換了 OS、backend 與平台,所以先不定因。能確定的只有一件事:**在自己機器上觀察到,不等於它是工具的普遍規律。**換台機器,先做一次順序反轉再決定能不能用逗號串。

但那個 ± 不等於「明天重開五次還拿得到同一個數」。這是兩層,用兩種口徑報:process 內就用 llama-bench 自帶的標準差;跨 process 則報中位數與全距。

而這件事也看機器。同一組量測,T2 四次 process 的 pp 全距只有 0.25%;T1 五次是 47.9%——但那是被一筆冷卻不足的 run 撐開的,把它拿掉,其餘四次是 5.5%。異常值不刪,只是要說清楚它異常在哪。

真正會翻盤的是機器狀態:同機同參數連跑五次不冷卻,pp512 從 583 掉到 440,少了近四分之一——比這篇在意的所有版本差異都大。

所以跑之前先確認沒有其他 CPU 或 GPU 重負載,關掉瀏覽器、IDE 與會吃統一記憶體的程式。別把 load average 當門檻——load 0.5 的機器照樣可能有東西在吃 GPU。Mac 記得插電。

賽前預測:用帳面下注

開表前先押注。用純帳面:546 對 504 = 1.08,tg 該幾乎打平;pp 不用賭,4070 Ti 大勝。

但這一注我自己都不信——表一已經算過,M4 Max 只吃到 546 的 58%。決勝的不是帳面比,是兩邊各自的折扣率。

開表:賽前那一注輸了

Day 02 欠的 T1/T2 兩排,今天補齊:

機器 backend fa llama.cpp rev. binary pp512(t/s) tg128(t/s)
T1 M4 Max 128GB MTL,BLAS off b10488 官方 macOS arm64 530.52 [368.7–545.4] 40.83 [30.2–41.7]
T2 RTX 4070 Ti CUDA off b10488 官方 win-cuda 13.3 x64 3810.19 [3802.7–3812.3] 57.72 [57.71–57.72]

格式是中位數 [全距],取多個獨立 process 的均值:T1 五次、T2 四次。T1 那個寬全距來自一筆冷卻不足的 run,正好示範前一節在講的事;排掉它,其餘四次的 pp 全距是 5.5%。

pp 差 7.18 倍,跟官方兩表暗示的量級相符。但 tg 出事了:4070 Ti 快 1.41 倍,而我押的是「Mac 小勝一成以內」,方向與幅度都錯。帳面說 Mac 該小勝 1.08 倍,實測是反方向的 1.41 倍——這條缺口就是 Day 10 的全部主題。

還有一件要交代:Day 02 那張表量的是 Ollama 的端到端回應,今天這張量的是純引擎吞吐,口徑不同不能混填。Ollama 那一口徑挪到 Day 13。

那 5090 跟 RTX PRO 6000 呢

兩張卡我都沒有,答案只能從別人的數字來——那就更要說清楚每筆是什麼等級的證據。5090 好辦,它就在上面那張 Vulkan 表裡。RTX PRO 6000 沒這個待遇:整條 Vulkan 串一筆都沒有,公開數字全在另一條 CUDA 串,兩串不能相除,不進任何一張表。

https://ithelp.ithome.com.tw/upload/images/20260819/20183550Jhq4JubrcL.png
圖 5:把公開回報畫成分布帶,而不是各取一個數字比長短。兩條帶大幅重疊,「誰比較快」在這個口徑下沒有答案。

先看帳面:PRO 6000 的 Workstation 與 Max-Q 版都是 1792 GB/s,跟 5090 同一個數。所以對小模型的 batch=1 decode,合理的預期是兩張落在同一個性能級距,不是靠三倍 VRAM 自動快三倍。

但同頻寬不等於同速度。PRO 6000 有 24,064 個 CUDA core,比 5090 的 21,760 多 10.6%;而且同叫 PRO 6000,600W 的 Workstation 與 300W 的 Max-Q 算力上限也不同——官方 FP32 分別是 125 與 110 TFLOPS。連 PRO 6000 自己都不能只靠那個 1792 GB/s 當成同一張卡看。

公開實測支持「同級距」:同樣 CUDA、FA 開、Llama 2 7B Q4_0,PRO 6000 六筆落在 270–289 t/s,5090 十筆落在 249–315 t/s——PRO 6000 整條帶都在 5090 帶裡面,排名就是在報雜訊。

更狠的在圖 5 下半:同一位投稿者、同卡、同 rev、同一天重開四次,tg 全距 7.1%;而每筆表上印的 ± 只有 0.1% 到 1%。那個 ± 沒有撒謊,它只是回答了另一個問題。

但別把「同級距」誤讀成「一定打平」。GamersNexus 用 LM Studio 量過一整套(引擎不同,只看方向):8B 蒸餾模型兩張並列 81 tok/s,Phi-4 的 8-bit 卻是 62 對 51——那顆兩張都塞得下,多的是算力不是容量。斷崖在後面:Gemma 3 27B,PRO 6000 是 29,5090 只剩 5

這就是本系列的兩條線:**速度線上,單靠頻寬規格分不出高下;容量線上,96GB 對 32GB 才是確定性的差距。**Day 07 在 Mac 那側講過同一件事。

業界視角:把基線鎖進 CI

上面那六根釘子在業界不是部落格素材,是 CI 的 job spec:固定 model 檔、固定參數、固定 build 流程,升級引擎或換驅動都觸發。門檻要先量出這台機器自己的變異再往上設,不然你會天天收紅燈然後開始無視它。

今天的實驗需要什麼

一台 Apple Silicon Mac 或一張 NVIDIA 卡就能跟。Gemma 4 12B QAT 要 6.48 GiB,8GB 的卡卡住的話就換 E4B 或任何一顆 8B。Mac 與 Windows 都有官方預編譯包,解壓即用;Linux 要自己編一次。填表時 backend 與 fa 欄記得寫。

小結

  • 官方兩張表各自都對,直接互比就錯:沒控的是 backend、source revision 與回報來源。模型反而不在裡面——兩串刻意用了同一顆 Llama 2 7B Q4_0。
  • 形狀有資訊:pp 拉開 5.62 倍、tg 只拉開三成。但倍率跨了 backend 與 revision,不能當算力或頻寬的量測。
  • 公平對照六根釘子:同 GGUF、同 source revision、同參數、單卡、backend 欄照抄、-fa 寫死。跨平台本來就留不下「只有硬體」一個變因。
  • 貼表要帶 ±,還要分兩層口徑:process 內用標準差,跨 process 報中位數與全距。而且看機器——T2 四次 process 的 pp 全距 0.25%,T1 排掉異常那筆也還有 5.5%。
  • T1 對 T2:pp 差 7.18 倍,tg 卻是 4070 Ti 快 1.41 倍——我押「Mac 小勝一成」,方向就錯了。差額 Day 10 算。
  • 「第二輪必慢」我只在 T1 重現,T2 沒有:在自己機器上觀察到,不等於它是工具的普遍規律。
  • 5090 與 PRO 6000 帳面頻寬相同、兩條帶重疊;但同頻寬不等於同速度——連 PRO 6000 的 WS 與 Max-Q 都不一樣。真正的分水嶺是塞不塞得下。

明天,Day 09:權重塞得下 ≠ 跑得動——KV cache 才是天花板。今天整篇都在比「一個人問一句」的速度;明天把 context 拉長、人數加上去,你會看到:權重只是一次付清的固定成本,真正會隨 context 與並發一起長大的,是 KV cache。

咱們明天見。


上一篇
Day 07 - 128GB 只能用 96GB?Mac 統一記憶體的潛規則
下一篇
Day 09 - 權重塞得下 ≠ 跑得動:KV cache 公式與 GQA、MQA、MLA
系列文
不是模型太慢,是你沒算過這筆帳:地端 LLM 工程實戰 30 天9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言