昨天結尾說「萬事俱備」。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 倍。勝負已分?慢著。這個除法本身就是錯的。

圖 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 某卡」的文章犯的正是這個錯。先問這些數字是不是同一場比賽跑出來的。
不可直比,不代表沒有資訊,比例的「形狀」會說話。

圖 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。這張表回答的是兩套平台各出主場後端誰快。

圖 3:六根釘子釘死可控制項,剩下的是整包平台變因——M4 Max 加 Metal 對 4070 Ti 加 CUDA。backend 欄照抄工具輸出。
ollama pull 的 blob 跟 HF 上同名的 Q4_K_M 根本是兩個不同的檔。-p 512 -n 128,重複 10 次。CUDA_VISIBLE_DEVICES 鎖死,別讓引擎自己決定切不切。MTL,BLAS 就寫 MTL,BLAS,不要潤色成「Metal」。-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 內重複測試的標準差,別在貼進文章時刪掉——一個沒有 ± 的數字,你分不出它是結論還是雜訊。

圖 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 好辦,它就在上面那張 Vulkan 表裡。RTX PRO 6000 沒這個待遇:整條 Vulkan 串一筆都沒有,公開數字全在另一條 CUDA 串,兩串不能相除,不進任何一張表。

圖 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 的 job spec:固定 model 檔、固定參數、固定 build 流程,升級引擎或換驅動都觸發。門檻要先量出這台機器自己的變異再往上設,不然你會天天收紅燈然後開始無視它。
一台 Apple Silicon Mac 或一張 NVIDIA 卡就能跟。Gemma 4 12B QAT 要 6.48 GiB,8GB 的卡卡住的話就換 E4B 或任何一顆 8B。Mac 與 Windows 都有官方預編譯包,解壓即用;Linux 要自己編一次。填表時 backend 與 fa 欄記得寫。
-fa 寫死。跨平台本來就留不下「只有硬體」一個變因。±,還要分兩層口徑:process 內用標準差,跨 process 報中位數與全距。而且看機器——T2 四次 process 的 pp 全距 0.25%,T1 排掉異常那筆也還有 5.5%。明天,Day 09:權重塞得下 ≠ 跑得動——KV cache 才是天花板。今天整篇都在比「一個人問一句」的速度;明天把 context 拉長、人數加上去,你會看到:權重只是一次付清的固定成本,真正會隨 context 與並發一起長大的,是 KV cache。
咱們明天見。