~/blog/minimax-h3-on-modded-2080ti-22gb

改裝 2080 Ti 22G · part 11

[趣味競賽 進階 #11] 什麼?2080 Ti 居然跑得動 MiniMax-H3,還生得出 1080p 有聲影片

cat --toc

TL;DR

一張 2018 年的改裝 2080 Ti 22G(sm_75)生出 15 秒 1080p 有聲影片,23 分 03 秒。組合是 INT8 擴散模型 + W4A4 文字編碼器,--reserve-vram 4SageAttention 照開——log 裡會一直印 fallback 訊息,但它還是比關掉快 16.8%(133.37 s vs 160.25 s)。看秒數,不要看訊息。

🔊 開聲音。1920×1080、362 幀、15.08 秒,畫面和音軌出自同一次生成。這支片是一張 2018 年的顯卡花 23 分 03 秒算出來的。

白話版:老顯卡跑不動新模型,卡住的通常不是算力

大家講顯卡都在比「快」。但一張顯卡跑不跑得動一個 AI 模型,第一關其實不是快不快,是裝不裝得下

真正在算畫面的那一塊,得整個放進顯卡自己的記憶體裡才動得了。放不下的時候不會慢慢變慢,是直接不能跑,畫面上跳出一行紅字然後結束。

所以老卡的困境長這樣:它算得比新卡慢,那是意料中的事,多等一會就好;真正把它擋在門外的是容量不夠,而容量不夠沒有「多等一下」這個選項。

原尺寸的 33B 模型放不進 22.5 GB 的車廂,那不是慢是根本不能跑;量化成 INT8 之後剛好放得下,代價只是要等

這張 2018 年的卡被改裝過,記憶體從 11 GB 加倍到 22 GB。而這個模型的四個檔案在磁碟上是 38 GiB。這篇要講的就是它們怎麼輪流上場,以及輪流的時候哪些直覺是錯的。


前言

租貨車的時候大家都在比馬力,實際上決定你一趟能搬多少的是後車廂的立方數。

這張卡也一樣。MiniMax-H3 是 33B 的影音同步生成模型,畫面和聲音出自同一次前向傳播。我在一張 5090 上跑通它之後,很自然想知道下限在哪——手上剛好有一台插著改裝 2080 Ti 的機器,22,528 MiB,2018 年的 Turing 架構。

跑得動。這篇先給完整配置,再給實測的速度和畫質,最後才是一路怎麼試出來的。

這一組配置就會跑

環境這三個版本是綁在一起的,任何一個升上去 SageAttention 就編不起來:

torch 2.6.0+cu124 · triton 3.2.0 · sageattention 1.0.6

python main.py --listen 127.0.0.1 --port 8188 \
  --reserve-vram 2 --use-sage-attention     # 15 秒的片要改成 4

四個模型檔。看最右邊那一欄,那是它們在 22 GiB 的卡上塞得下的原因:

檔案磁碟大小什麼時候在卡上
文字編碼器 Qwen3-VL-32B(W4A4)13.20 GiB開頭跑一次,跑完就卸掉
擴散模型 DiT(INT8)19.53 GiB取樣全程常駐
影片 VAE(fp16)4.85 GiB最後解碼才載進來
音訊 VAE(fp32)0.56 GiB同上
合計38.14 GiB但從來不同時在卡上

那 38 GiB 不是一口氣要吃掉的量,是四個房客輪流住同一間房。

三個階段的常駐狀況:讀提示詞時只有文字編碼器在卡上,取樣時換成 DiT 加 activation 把 22 GiB 填滿,最後才輪到兩顆 VAE真正決定這張卡行不行的是中間那一列:DiT 常駐 19.53 GiB,而卡有 22。 剩下 2.47 GiB 要放取樣過程產生的所有中間資料——這個餘額決定了下面三件事。

取樣參數照抄:

取樣   res_multistep + simple · 20 步 · fps 24
解析度 960×540 生成 → 放大到 1920×1080
縮放   SCALE = 1.0(不縮放)

為什麼是 --reserve-vram 4

這個旗標從名字看像是「留幾 GB 給作業系統」。它確實有這個作用,但在這張卡上它同時是取樣階段的 activation 預算

同一支 15 秒的片,三個值三種下場:

--reserve-vram結果
243.5 秒後 torch.OutOfMemoryError,峰值 21,948 / 22,528 MiB
4成功,1,383.0 秒
8不 OOM,但 9.5 GB 權重被丟去串流,一步跑五分鐘還沒完

留太少,activation 沒地方放;留太多,權重塞不進去,只好邊算邊從系統記憶體搬。短片可以設 2,15 秒的片一定要 4 —— 這是唯一一個你會反覆調的值。

22.5 GB 要同時裝權重和 activation:留 2 放不下就 OOM,留 8 權重被擠出去串流,4 是兩邊都剛好的位置

為什麼 DiT 一定要 INT8,而文字編碼器可以 W4A4

同一個量化格式,兩顆模型的答案相反:

  • DiT 必須 INT8。 W4A4 版本的 microbenchmark 相對誤差是 0.2005,INT8 是 0.0110——差 18 倍。畫面上是整片彩色撕裂。
  • 文字編碼器 W4A4 沒問題。 它只產生 conditioning,量化誤差影響遠小於 DiT。從約 24 GiB 掉到 13.2 GiB,省下的載入時間值 8.6%(3 秒的片 277.7 → 253.8 秒)。

別把「W4A4 不行」記成通則,它只對 DiT 不行。

為什麼 SageAttention 要開,而 log 裡的錯誤不用理

開了之後每一次 attention 呼叫都會印這行:

[ERROR] Error running sage attention: out of resource: shared memory,
Required: 67584, Hardware limit: 65536 ... using pytorch attention instead

而啟動 banner 照樣印 Using sage attention。看起來就像壞掉了——但秒數說它沒有:

版本秒數
SageAttention 照開(log 有 2 次 fallback)133.37✅ 最快
SDPA(整個關掉)160.25慢 16.8%

💡 部分 fallback 不等於沒生效。 sm_75 只在某些形狀上超過 64 KB 共享記憶體上限而退回 SDPA,其餘呼叫照樣走快路。驗它有沒有用要看秒數,不要看 log。

跑起來是什麼樣子:23 分 03 秒,畫質跟 5090 同規格

這張表看最下面那一列——那是這張卡的實際成品,不是估算:

工作秒數產物
3 秒 · 960×540 → 1080p253.81920×1080 · 73 幀
15 秒 · 960×540 → 1080p1,383.01920×1080 · 362 幀 · AAC

23 分 03 秒,平均 88.87 秒跑一步。ffprobe 驗過:1920×1080、24 fps、362 幀、AAC 32 kHz 立體聲、15.083 秒。跟我在 5090 上生的那支規格完全一樣,只是慢 4.4 倍。

⚠️ 那個「4.4 倍」值得放在心上:同一支片 5090 要 314 秒。這張卡不是做不到,是要你等。 這兩件事在排工作的時候完全不同——「做不到」你會去買卡,「要 23 分鐘」你會把它排去跑整夜的批次。

畫質方面,最意外的是放大這一步。NVIDIA 的 Video Super Resolution 在這張 2018 年的卡上跑得動,而且不慢:

機器只算放大節點每幀
RTX 5090(sm_120)11.17 s / 362 幀30.9 ms
改裝 2080 Ti(sm_75)3.45 s / 73 幀47.3 ms

每幀只比 5090 慢 1.53 倍。 這一段是用上一篇那個快取分離法單獨量出來的。

兩個階段分開量:生成慢 4.4 倍,但放大只慢 1.53 倍——老卡輸的是取樣,不是整條產線

🔊 開聲音。同一個 seed、同一組提示詞,只換放大節點。左邊 Real-ESRGAN,右邊 RTX VSR,兩支都是這張 2080 Ti 生的。

⚠️ 但品質模式的排序在兩台機器上是反的。5090 上 ULTRA 產出的檔案最大(細節最多),這張卡上 ULTRA 反而最小(643 KB vs HIGHBITRATE_ULTRA 的 703 KB)。原因我還沒查清楚。模式要在你自己的機器上挑,別抄別人的。


進階:一路是怎麼搞定的

不讀這節不影響使用。上面那組配置不是一次試出來的——這節是繞過的路,包含一條走到底才發現該回頭的。

我繞的最遠的那條:看到 fallback 就想去修它

思路是這樣:

  1. log 每一次 attention 呼叫都印 fallback,而啟動 banner 說 Sage 在用
  2. 想說把它修好,Sage 就能全速跑
  3. 推導出 Required: 67584 的來源,把 BLOCK_M 從 128 改成 64,訊息真的消失了
  4. 量秒數:176.2 秒,比什麼都不做還慢

原因是 BLOCK_M 減半讓 block 數加倍,排程和邊界處理的開銷比省下來的還多。

而更早的一個判斷也跟著錯:我拿「改過 kernel 的 Sage」去代表「Sage」,得出「Sage 在 Turing 上沒用」寫進知識庫。真相是完全沒改過的那版一直是最快的。

版本秒數
SageAttention 原封不動(2 次 fallback)133.37✅ 最快
SDPA(整個關掉)160.25基準
SageAttention 改過 kernel(0 次 fallback)176.2❌ 最慢

三個版本的秒數:原封不動 133.37 秒最快,關掉是 160.25 秒,改過 kernel 反而變成 176.20 秒最慢

這三個數字是交錯量的(A B C A B C A B C,不是分三批各跑三次),argv 逐字相同,唯一的變因是那個旗標。對照臂三發的全距是 1.15 秒(0.7%),所以那 16.8% 是雜訊底線的 24 倍。

💡 fallback 訊息是雜訊,不是病徵。追著它去改 kernel,等於為了讓 log 好看而犧牲效能。

下面是那個推導。它本身是對的、也是原創的,只是結論從「我修好了」變成「我不該修」。

67,584 是怎麼算出來的

Hardware limit: 65536 就是 64 KB,Turing 每個 SM 的共享記憶體上限。要的 67,584 比它多 2,048 個位元組——只差 2 KB。三個轉折把它釘死:

轉折一:換解析度,數字不變。 三個不同的解析度,Required 三次都是完全相同的 67,584。序列長度變了而需求沒變,代表這是 kernel 裡的常數。

轉折二:拆解成分毫不差。 SageAttention 1.0.6 的 h96 kernel 預設 BLOCK_M=128, BLOCK_N=64。三種精度的暫存加起來,每個 block 要 (128×4 + 64×2 + 64×1) × head_dim,也就是 704 × head_dim:

BLOCK_M × 96 × 4 (fp32)  = 128 × 96 × 4 = 49,152
BLOCK_N × 96 × 2 (fp16)  =  64 × 96 × 2 = 12,288
BLOCK_N × 96 × 1 (int8)  =  64 × 96 × 1 =  6,144
                                  合計   = 67,584

而且 96 是唯一解:head_dim 128 會算出 90,112,64 會算出 45,056。所以走進這支 kernel 的那條 attention,head_dim 就是 96——core.py:98if headdim == 96: 正是把它分派到這裡的那一行。

⚠️ 但那條不是主幹的 DiT。 H3 官方 transformer config 寫的是 attention_head_dim = 128,所以這個 96 來自產線上的別的地方——是哪一個我還沒查出來。

原因是:sageattention 1.0.6 沒有任何架構分支,它的 block 大小假設的共享記憶體遠大於 Turing 的 64 KB(Ampere 依型號 100–164 KB、Hopper 228 KB)。

三行加起來剛好 67,584,比 Turing 每個 SM 的 65,536 多出 2,048 個位元組,所以每次呼叫都退回 SDPA

轉折三:錯誤訊息的建議有一半是錯的。 訊息說 Reducing block sizes or num_stages。我先改 num_stages(4 → 2),完全無效Required 一個位元組都沒動。只有 block 大小有用。

改對了參數,改錯了檔案

第一次改完沒效,我以為推導錯了。實際上是 core.py:98 把那條呼叫分派到 h96 專用的 kernel,而我改的是 head_dim 128 那支。後來又踩一次:只改 h96 也無效,要把 sageattention/ 底下全部六支 kernel 的 BLOCK_M 都改掉,再清 ~/.triton/cache__pycache__ 重啟,訊息才消失。

💡 「改了沒效」有兩種原因:改錯參數、改錯檔案。這兩個我都踩過,而它們的症狀一模一樣。

VSR 我也判錯過一次

NVIDIA 的 Video Super Resolution 我原本判它在這張卡上不能用,理由是舊版 Maxine SDK 的支援清單(全是 A100/H100 那一級的卡)。

拿錯的尺去量。新的 nvidia-vfx wheel 其實支援 Turing 到 Blackwell,裝起來就跑了——上面那個每幀 47.3 ms 就是它。

W4A4 為什麼不行

順帶把這條收掉,免得有人重走。思路是這樣:INT8 的 DiT 19.53 GiB,把卡塞得太滿——而 Turing 有 INT4 的路,量化成 W4A4 能再砍一半。

  • 直接量化炸掉:權重超出 INT4 的表示範圍,根本跑不起來
  • 所以要縮放。 SCALE 從 1 一路調到 1/16、1/64、1/256,總算讓它跑得完
  • 影片是生出來了,但不能用:整片彩色撕裂,霓虹散景全糊掉
  • microbenchmark 對得上眼睛看到的:相對誤差 0.2005,而 INT8 是 0.0110——18 倍

⚠️ 但這只是我試到的地方。 四個 SCALE 值畫面壞的方式一模一樣,把放大器整個拿掉也一樣壞,所以我在這裡停手換回 INT8。這不等於 W4A4 在這顆 DiT 上永遠不行——只等於我走過的這幾條路都沒生出能用的畫面。那份權重我已經刪了。

常見問題

MiniMax-H3 在 22 GB 的顯卡上跑得動嗎?
跑得動。一張改裝 2080 Ti 22G(sm_75、22,528 MiB)生一支 15 秒 1080p 有聲影片要 23 分 03 秒。條件是擴散模型用 INT8、文字編碼器用 W4A4,而且 ComfyUI 的 --reserve-vram 要設成 4。四個檔案在磁碟上共 38 GiB,但它們輪流上場,從不同時常駐。
ComfyUI 的 --reserve-vram 到底在留什麼?
它不是「留給作業系統的記憶體」,它同時是取樣階段的 activation 預算。同一支 15 秒的片,設 2 會直接 OOM、設 4 剛好跑完、設 8 不會 OOM 但 9.5 GB 的權重被丟去串流,一步要跑五分鐘以上。這是這張卡能不能做長片的唯一旋鈕。
SageAttention 在 Turing(sm_75)上有用嗎?
有用,而且值 16.8%。它會在某些形狀上超過 64 KB 共享記憶體的上限而退回 PyTorch,log 裡看得到 fallback 訊息——但那不代表它沒生效,其餘呼叫照樣走快路。要驗它有沒有用請看秒數,不要看有沒有 fallback 訊息。
RTX Video Super Resolution 在 Turing 顯卡上跑得動嗎?
跑得動。現行的 nvidia-vfx wheel 支援 Turing 到 Blackwell,舊版 Maxine SDK 那份只列資料中心卡的支援表已經不適用。單獨量放大那顆節點,2080 Ti 是每幀 47.3 ms、5090 是 30.9 ms,只慢 1.53 倍。但品質模式的排序兩張卡是反的,要在自己機器上挑。

接著讀

  • 2026-07-18
    [趣味競賽 進階 #10] 同一張 2080 Ti,一台生圖慢 3.4 倍——兇手是啟動 log 裡一行 dtype fallback

    兩台機器裝的是同一張改裝 2080 Ti 22G,一台生 Z-Image 暖機 median 46.08s,另一台一樣的卡只要 ~9.5s。當天記錄上的結論很硬:46s 就是這張卡的天花板——GPU 全程 100%、模型常駐、卡也對。但我記得另一台一樣的卡跑過 10 秒(那筆沒進知識庫),一查發現數據沒騙人、只是漏了一塊。真兇不是矽晶片,是 ComfyUI 啟動 log 裡一行精度 fallback:Turing 沒有 bf16 tensor core,ComfyUI 為了保精度把權重留在 fp32,算得慢 3.4 倍。這篇是把兇手從『100% GPU 看起來就是天花板』這個舒服答案裡挖出來的偵探故事——外加兩個誠實提醒:這個旗標不省 VRAM、對影片模型也完全沒用。

  • 2026-07-10
    [趣味競賽 進階 #9] 0xc0000409:當我的 AI 服務靜默暴斃,而 log 把死因吃掉了

    一台 headless Windows AI 主機上的本地腦,偶發在真實負載下無聲無息地死掉:client 吃到短暫 503,服務自己又活回來,應用程式 log 一片空白。最氣的是真兇不是模型本身,是我自己——重啟前 log 把 crash 當下那行死因截掉了。這篇是把死因從『被自己的 log 掩埋』裡挖出來的偵察故事:為什麼會自我重啟的服務最會掩埋自己的死因、為什麼要先看 OS 層的 Event Log、以及 0xc0000409 到底是什麼。誠實在先:根因我還沒 100% 確認,這是還在查的紀錄,不是結案報告。

  • 2026-07-03
    [趣味競賽 進階 #8] 為什麼 30 tok/s 體感比 14 還慢:TTFT 才是你真正感覺到的速度

    我把家裡那顆 agent 腦的 decode 從別台的 14 tok/s 換成這台 30,結果它感覺更慢。tok/s 這個數字我盯了一年,原來它量的是「吐字多快」,不是「多久才開始吐」。在一顆 hybrid 模型上,一次 cache miss 就把整段 prompt 從頭重算——同一台機、同一顆腦,暖機 2.6 秒、冷掉 216 秒。這篇用 Hina 自己的 log 講:為什麼該盯的是 TTFT。

  • 2026-07-02
    [趣味競賽 進階 #7] 在慢腦上開漸進式串流,我把自己撞進了 Telegram 的 flood control

    我想讓慢吞吞的本地 agent 少一點等待焦慮,於是開了 Telegram 的「逐字浮現」串流——每隔幾百毫秒就改寫一次訊息。結果一個一百多秒的回應發出上百次編輯請求,正面撞進 Telegram 的 flood control,被罰等兩百多秒,整個 bot 卡死,連最終答案都送不出去。這篇講一個短而毒的教訓:慢模型不該開漸進式串流,一次性送最終答案反而穩。附 log 實證。

不想錯過新文章?

訂閱我確保不漏接!

隨時一鍵退訂。