iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
AI Engineering

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

Day 11 - 70B 上 M4 Max:那些 20-28 tok/s 到底是怎麼來的

  • 分享至 

  • xImage
  •  

動筆寫這個系列之前,我 Google 了「M4 Max 70B tokens per second」。搜尋第一頁的答案出奇一致:20-28 t/s。有表格、有結論,排版專業極了。

昨天那條除法給的是 12.8。

https://ithelp.ithome.com.tw/upload/images/20260822/20183550bLjoDroqd5.png
圖 1:昨天算的是綠色那條。

到底誰算錯了?

昨天我停在「條件一定有東西不一樣」,然後列了四個要問的問題。今天把那四個問題真的問完。

在那之前,我得先修一句自己說過的話。Day 01 的路線圖裡我寫:「網路上查得到的某些數字在物理上根本不可能。」那句說得太滿了。 把這篇寫完之後我才看清楚,那條上界是有前提的,而前提是可以被合法打破的——追出那 20-28 的來源,比判它真假有用得多

先把上界的前提說清楚

昨天那條除法是這樣來的:decode 每產一個 token,GPU 得把這個 step 要讀的權重串流一次,所以單流的 tok/s 天花板 ≈ 記憶體頻寬 ÷ 每個 step 要讀的權重。

先把權重換成一手數字。這次不用推:bartowski 那顆 Llama-3.3-70B-Instruct-Q4_K_M.gguf 是 42,520,398,816 bytes,十進位 42.52 GB——就是 Day 07 量過、Day 09 拿去算容量的同一顆檔。

546 ÷ 42.52 = 12.8 tok/s

反推一次,42.52 × 8 ÷ 70.55 = 4.82 bpw。這再次驗證 Day 05:Q4_K_M 沒有一個可以跨模型硬套的固定 bpw——要精確,就直接看這顆檔案。(常被引用的 Artefact2 那張表列 4.83,量的是 Mistral-7B。)

重點是這條上界有前提,而且至少有四個要先檢查:

  • bs=1 的單流
  • 沒開 speculative decoding
  • dense 模型(MoE 每 step 只讀被啟用的那幾個 expert,shared 層、attention、router 仍然照讀)
  • 量的是 decode,不是 prefill

四個只要破一個,實測就可以合法地站到 12.8 之上。所以看到超標的數字,第一步不是判它假,是問它踩到哪一條。

https://ithelp.ithome.com.tw/upload/images/20260822/20183550aFHDpubDKD.png
圖 2:三個劇本,各自要看不同的欄位才驗得出來。

劇本一:那不是 70B

反推最直接。這裡要用一個兌現率,而 Day 10 的規矩是:沒實測過的機器用 50-80% 做 sensitivity,量到了就收成一個點。M4 Max 就是我那台 T1,已經量過 52%,所以這裡不猜區間。

代回去問「什麼重量的模型才跑得出 20-28」:

546 × 0.52 ÷ 28 = 10.1 GB
546 × 0.52 ÷ 20 = 14.2 GB

10 到 14 GB。 這個區間裡有 gpt-oss-20b 的原生 MXFP4(13 GB),有 Gemma 4 12B 的 Q8,就是沒有 70B 的 42.52。

差了三倍的重量,不是一般調參數能補回來的。如果那個 20-28 是普通 dense decode,那它對應的就不是這顆 42.52 GB 的 70B。

這一步有個我自己要認的假設:那 52% 是在 6.96 GB 的 Gemma 4 上量的,這裡卻拿去除一顆重六倍的模型。Day 10 才說過這種係數不能跨模型直接搬——固定成本的佔比會變。所以 10-14 GB 是量級判斷,不是精確值。

但反過來看就很硬:真要讓 42.52 GB 跑出 20-28,兌現率得是 156% 到 218%。 超過 100% 就不是效率問題了,是前提被破了——這正好把我們推向劇本二。

劇本二:開了 speculative decoding

這條是真的能突破 12.8。

speculative decoding 讓便宜的來源先猜幾個 token,主模型把這幾個候選放進同一個 verification batch 一起處理——權重讀取的成本因此被攤到多個候選頭上。llama.cpp 官方的說法是 batch 處理 n 個 token 比循序做 n 次有效率;接到 Day 10 那條線上,就是「每 step 要讀的權重」被幾個 token 分攤。

代個數字看量級。假設某套 speculative 系統平均每次 verification 最後能換回約 2.8 個輸出 token:

546 ÷ 42.52 × 0.52 × 2.8 ≈ 18.7 tok/s

但這個 18.7 只是量級示意,不是預測。 它沒扣掉猜測本身的成本、verification 的計算成本、被拒絕的 token,也沒管 batch 變大之後效率不會線性乘。而且它自己還沒到 20。

所以這條只能說到這裡:speculative 有能力打破 12.8 這道 plain-decode 上界,但我沒有任何證據說那些 20-28 就是這樣跑出來的。

要驗也不難,只是要問對欄位。llama.cpp 現在有 10 種 speculative 方法,其中 5 種 ngram 路線完全不需要第二個權重檔,--spec-default 開的就是 ngram-mod。所以該問的不是「draft model 是哪顆」,是 --spec-type、候選長度與接受率;只有需要另一個權重檔的那四種,才要另外交代 draft 是哪顆。

順帶結掉一個疑慮:llama-bench 本身沒有 speculative 選項,要量得換別的工具。所以 Day 08 到今天這批 llama-bench 的 tg,不會被 speculative 混進來。

劇本三:那個 measured 對不上方法頁

前兩個劇本都要對方交代條件才成立。這一個相反——欄位看起來填好了,填的卻不是那次跑測的東西。

LLMCheck 這個 Apple Silicon 地端 LLM 索引就是現成的案例。它的部落格逐字寫著「measured on 128GB configurations using Ollama with Q4_K_M quantization」,表上 Llama 3.3 70B Q4 給 ~22 tok/s。runtime、量化、記憶體三個欄位都有,比多數頁面交代得還多。

但同一個網站的資料政策逐字寫著:「No figure is claimed as first-party measured.」

https://ithelp.ithome.com.tw/upload/images/20260822/201835505qhoHmjN33.png
圖 3:changelog 還留了個案——Llama 3.1 8B 曾被列成 75 tok/s,而那台 M4 的頻寬只撐得住 26。

它 2026-08-15 拿自己公布的公式回頭稽核,逐字:

Before that, 63 of them implied a model reading its weights faster than the memory bus could deliver them — not achievable on any hardware.

另一頁走的是另一種路。CurrentAffair 那篇寫「We tested … using the MLX framework and llama.cpp」,表上 Llama 3.3 70B 給 28.4 tok/s;同一頁也寫了 546 GB/s 與「~43GB RAM」。即使拿它自己寫的 ~43 GB 當粗略分母,plain dense decode 的上界也只有約 12.7。 28.4 要成立,那頁至少還少交代一個關鍵條件。

所以「數字是編的」講得太粗。更常見的是:估算被貼上實測的標籤,或者舊版的估算留在原地沒人回頭掃。

那正確答案落在哪

推算完別人的,回頭推自己的。70B Q4_K_M 在 Apple Silicon 上有第三方數據可以借:XiongjieDai 的 GPU benchmark 表,llama.cpp Metal 後端,tg 1024 欄,該節 2024-05-08 加入。

同一張表上有三筆:M1 Max(400 GB/s)4.09、M3 Max(400)7.53、M2 Ultra(800)12.13。

同表同模型(Llama 3 70B Q4_K_M)同後端同欄位。該表未鎖 commit、未標 build、未給 ±,量化檔為作者自行轉換;M1/M3 Max 是筆電,M2 Ultra 是 Mac Studio。

我本來想用內插:546 落在 400 與 800 之間,拿 M3 Max 與 M2 Ultra 夾一下,得 9.2。

https://ithelp.ithome.com.tw/upload/images/20260822/20183550HzzcSFBVmn.png
圖 4:兩條虛線是同一個內插法,只換左端點。

這條路被同一張表的第一列推翻了。 M1 Max 的帳面頻寬也是 400,卻只跑 4.09。左端點一換,同一個內插法給出的是 7.0 不是 9.2:

(400, 7.53) 與 (800, 12.13) 夾 546 → 9.2
(400, 4.09) 與 (800, 12.13) 夾 546 → 7.0

同一個 x 對到兩個相差 1.84 倍的 y,這根本不是一條可以內插的線。內插這條路我撤回。

它也提醒我別急著解釋那 15 個百分點。我原本想歸給 UltraFusion 雙 die——但 M1 Max 是單 die,兌現率只有 43.5%,比雙 die 還低。

而且 Day 10 那條 每 token 時間 = 權重 ÷ B + 固定成本 就擬合得了:從 M3 Max 反解得 26.5 ms,原封不動代進 M2 Ultra 是 12.6,實測 12.13。

一個固定成本項就夠近了,所以沒必要先把差距歸給封裝。 但這只證明「簡單模型擬合得了這兩點」,不代表那 26.5 ms 在 M2 Ultra 真是同一筆。兌現率不是硬體屬性。

剩下一條路還站得住:同串 #4167 的 7B Q4_0,M4 Max 對 M3 Max 是 83.06 對 66.31,快 25%。套到 70B:7.53 × 1.25 ≈ 9.4。這條也要打折看——那兩筆的 build 一個是 8e672efe、一個是 55978ce,並沒有真的鎖在同一版。

折扣法給 6.7,世代比率給 9.4。兩條都離 20-28 很遠,但它們也還不是答案——在自己跑之前,我押高個位數到 10 左右。下一節開獎。

第三層:我自己跑

推算做到這裡已經可以下注了,但系列規矩是規矩。而且這一跑正好能結掉上一節那個假設:52% 到底守不守得住到 42.52 GB。

跑了。我押高了。

https://ithelp.ithome.com.tw/upload/images/20260822/20183550nIHrIVSdSl.png
圖 5:三條推估對上實測。

Llama 3.3 70B Q4_K_M @ T1 中位數 全距
pp512 70.96 [69.32–71.77]
tg128 7.00 [6.71–7.06]
tg1024 3.73 [3.57–3.82]

三個獨立 process,macOS 26.2、llama.cpp 官方 release b10488(9d77fa172)、-p 512 -n 128,1024 -r 3 -ngl 99 -fa off。跑之前 ollama ps 確認無常駐,process 之間冷卻 240 秒。模型 42,520,398,816 bytes,sha256 32df3bac…。原始 log 在 repo 的 research/day11/

7.00,不是我押的「高個位數到 10」。三條路裡只有折扣法押在附近——Day 10 那個 52% 給的是 6.68,離實測中位數差 4.6%,離全距下緣只剩 0.03 tok/s(嚴格說還差一點點才進得去);世代比率的 9.4 與被我撤回的內插 9.2,都高了三成。

而且那個假設守住了:同口徑兌現率 54.5%(全距 52.2–55.0%)。在 6.96 GB 的 Gemma 4 上量到的係數,套到重六倍的 42.51 GB,只差兩個百分點。

「同口徑」這三個字等一下會變得很重要:Day 08 與 Day 10 的 T1 也都是 -p 512 -n 128,tg 同樣排在第二個測項。所以 7.00 對 52% 是同口徑比較,這個結論站得住。但 7.00 不是這台機器跑 70B 的純 decode 極限——單獨量 tg 是 8.86,對應的兌現率是 69.0%。同一台機器、同一顆模型,換個量法就換一個兌現率,這正好是昨天那個主題的第二個實例。

為什麼守得住?先拿同一台、同一個 build、同一組旗標的兩點解 Day 10 那條 每 token 時間 = 權重 ÷ B + 固定成本:12B 的 6.96 GB 跑 40.83、70B 的 42.51 GB 跑 7.00,得到 B_eff ≈ 300 GB/s、截距約 1.3 ms

再拿沒參與擬合的 8B(4.92 GB)回頭驗:預測 56.5、實測 58.4,差 3.2%。至少在這三顆、這套固定測序下,時間 = 權重 ÷ B_eff + 截距 貼得很好。 但這不代表那 1.3 ms 就是硬體上拆得出來的固定開銷——等一下你會看到,這套 benchmark 本身就有很大的順序效應。

那 tg1024 的 3.73 呢

只有 tg128 的一半。第一直覺是 KV cache,但單靠 KV 的原始讀取量解釋不了這個跌幅:Llama 3.3 70B 是 GQA,80 層、8 個 KV head、head_dim 128,F16 之下每個 token 的 KV 只有 0.31 MiB,跑到 1024 也才 320 MiB——相對於 39.59 GiB 的權重不到 1%。

而且前面那張第三方表就有反證:同一顆 70B 從 tg512 到 tg1024,M3 Max 只掉 1.6%、M2 Ultra 2.8%、M1 Max 5.8%。

所以我用 llama-bench-d——先把 KV 預填到指定深度,再只跑 128 個 token——拆了三組。

https://ithelp.ithome.com.tw/upload/images/20260822/20183550PGG2Hhs827.png
圖 6:綠線是平的,紅線是往上的。

綠線那組沒有重啟 process、沒有重新載入模型,只在每個測項之間加了 --delay 240。它幾乎是平的。所以重點不是 process,是冷卻。

紅線更狠:同樣沒冷卻,把深度倒過來跑,線是往上的。最深的 d=896 排第一,量到 7.27;最淺的 d=0 排最後,只剩 3.55。同一台機器上,最深的 context 比最淺的快了兩倍。

如果成因是 context,這條線不可能往上。所以結果主要受測項順序與持續運行後的狀態控制,不是 context depth。

Day 08 那個「第二輪必慢」今天縮小範圍了:不是 context,也不是重開 process;真正有影響的是上一輪跑了多久、冷卻了沒有。 240 秒就能恢復,高度指向 thermal 或 power throttling——但我沒量溫度、沒量 GPU 頻率,所以還差一支溫度計才能把那三個字寫死。(-fa on 重跑趨勢也不變,順便排除 -fa off 這個設定。)

在充分冷卻的口徑下,d=0→896 的落差是 −10.6% 與 −12.8%,跟第三方表那幾個百分點同量級。那是 context 深度的整體效果,不是純 KV 的成本。

所以我表上那個 tg1024 = 3.73 不能拿來談長 context

前面那張第三方表測的是 Llama 3 70B,不是 3.3。兩者參數量完全相同,決定每 step 要讀多少的 transformer 形狀也一致,bartowski 出的兩顆 Q4_K_M 只差 5 KB 的 metadata。

但 3.3 多了 llama3 RoPE scaling 與 128k context,而那個 scaling 低位置一樣生效。所以tg1024 比速度量級合理,但不是嚴格的 apples-to-apples,輸出品質更不能對照

跟跑這一局要一台 128GB 的 Apple Silicon Mac 加 45 GB 磁碟;64GB 機器改跑 32B Q4,公式照用。

# 1. 下載 Q4_K_M(42.52 GB)
huggingface-cli download bartowski/Llama-3.3-70B-Instruct-GGUF \
  Llama-3.3-70B-Instruct-Q4_K_M.gguf --local-dir models

# 2. 條件照 Day 08 那六根釘子
./llama-bench -m models/Llama-3.3-70B-Instruct-Q4_K_M.gguf \
  -p 512 -n 128,1024 -r 3 -ngl 99 -fa off

llama-benchsize 欄會印 39.59 GiB,那是 tensor 大小,比檔案少了約 10 MB 的 metadata 與 tokenizer。嚴格說,每個 step 要讀的是 tensor 不是整顆檔,分母該用它;但兩者只差 0.02%,546 ÷ 42.52546 ÷ 42.51 都是 12.8。跟上面的 42.52 GB 不是同一個數,別對到懷疑人生。

帶得走的:看到數字先問四件事

今天的方法比數字本身值錢。以後看到任何「某機器跑某模型 X t/s」的宣稱:

https://ithelp.ithome.com.tw/upload/images/20260822/20183550I4BQs0O9gz.png
圖 7:四個都對得上,再比速度。

先算天花板。超過它不代表假,代表它跟你算的不是同一組條件。

四件事全都對不上、又查不到 provenance,才是可以關掉的那種。

長 context 會再往下扣一些,但沒有想像中多——我量到 context 深到 896 是掉 10.6%。這招也只對 decode 有效,pp 是算力瓶頸。

小結

  • 今天真正要帶走的不是 9.x,是 12.8 的適用條件546 ÷ 42.52,前提是 bs=1、無 speculative、dense、量的是 decode。破一條就可以合法超標——Day 01 那句「物理上根本不可能」在此更正。
  • 超過天花板不等於造假。 先查模型、量化、speculative、decode 口徑;查完還對不上,再追 provenance——LLMCheck 自己就稽核出 63 筆舊估值快過記憶體匯流排。
  • 至於 M4 Max 的 70B 到底多少:實測 tg128 中位數 7.00(兌現率 54.5%)。我押的「高個位數到 10」押高了,只有 Day 10 那條自家實測的折扣法最接近——別人的表很難推你的機器;同機、同口徑量到的係數,這次反而最准。
  • 但今天被推翻的不只預測,還有我自己的量法:tg1024 那個 3.73 量到的是熱,不是 context。把深度倒過來跑,線會往上——最深的排第一比最淺的排最後快兩倍。加了冷卻之後,context 從 0 到 896 實際只掉一成。

明天,Day 12:換到桌子另一邊。Day 01 我押過「兩張卡的頻寬理論上可以疊加」,明天結帳——TP、PP、replica 三種切法的通訊帳,以及消費卡的兩張卡到底能不能直接講話。順帶要更正我自己記錯的一件事。

咱們明天見。


上一篇
Day 10 - 546 對 504 帳面同級,為什麼 4070 Ti 實測快 41%
系列文
不是模型太慢,是你沒算過這筆帳:地端 LLM 工程實戰 30 天11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言