~/blog/minimax-h3-rtx5090-speedup-vsr

MiniMax-H3 on RTX 5090 · part 2

[Benchmark] 625 秒砍到 314 秒,快了整整一倍:MiniMax-H3 在 RTX 5090 上該開的三個開關

cat --toc

TL;DR

同一支 15 秒 1080p 有聲片,625 秒砍到 314 秒,畫面沒有變差。三個開關:步數 20→14(−18.9%)、SageAttention 2.2.0(−18.8%)、放大器從 Real-ESRGAN 換成 RTX Video Super Resolution(−23.7%)。第三個最意外——放大那顆節點原本吃掉約 109 秒,換掉之後剩 11.2 秒。代價是 VSR 的 wheel 只有 x86_64。

🔊 開聲音。這支就是本文那條產線的產物:1920×1080、362 幀、15.08 秒,畫面和音軌出自同一次生成。從按下按鈕到這個檔案落地,314 秒。

白話版:AI 放大照片的時候,多出來的細節是它編的

生一支影片很貴,貴在畫布的大小。所以大家都用同一招:先用小畫布畫完,再把它放大到你要的尺寸。省下來的時間非常可觀。

問題出在「放大」這兩個字。傳統的放大就是把每個點變成四個點,糊掉是必然的。所以現在都改用 AI 放大——它看著模糊的畫面,把它回清楚的樣子。猜得好的時候,皮膚的毛孔、頭髮的絲、衣服的織紋都會回來。

但那些東西原本並不在畫面上。它是照著「真實的臉通常長這樣」猜出來的。

大部分時候你不會發現,因為猜得很像。可是只要原始畫布小到一個程度,猜的比例就會超過看到的比例,這時候畫面會出現一種很難形容的塑膠感——每一根頭髮都很清楚,但那不是他的頭髮。

從 480×270 放大四倍之後,臉上被圈起來的那些細節在原始畫布裡並不存在;檔案從 3.4 MB 漲到 7.5 MB,多出來的就是它補的

這篇有一半在講怎麼把放大這一步做得又快又不假,另一半在講那條界線落在哪裡。


前言

調車的人都盯著引擎,因為馬力數字最好看也最好講。但真正讓圈速掉下來的,常常是一個沒人去量的小地方——換掉它,速度就起飛了。

這條產線也一樣。上一篇把 MiniMax-H3 塞進一張 5090 之後,我調的全是引擎那一側:取樣器、步數、attention kernel。放大影片的那顆節點,我從頭到尾沒量過——因為本來以為相對生成沒那麼花時間。

直到我用一個取巧的方法把它單獨量出來:109 秒。整份 workflow 411.8 秒,放大自己吃掉四分之一。

換掉之後那顆節點只剩 11.2 秒。

你會拿到什麼:同一支片,時間對半,畫面沒有變差

這張表看最右邊那一欄就好。四行是同一支 15 秒 1080p 有聲影片,同一組提示詞、同一個 seed,一路疊上去:

組態秒數對上一行
20 步 + Real-ESRGAN625基準
步數改 14507.0−18.9%
加 SageAttention 2.2.0411.8−18.8%
放大器換成 RTX VSR314.0−23.7%

從 10 分 25 秒變成 5 分 14 秒。而且產物規格完全一樣——ffprobe 驗過兩邊都是 1920×1080、24 fps、362 幀、AAC 32 kHz 立體聲、15.083 秒。

畫質我等一下講。先講三個開關怎麼開。

開關一:步數從 20 改成 14,先省掉五分之一

範本給的是 20 步。這個數字沒什麼道理,就是保守。

改成 14 之後掉 18.9%,而畫面我看不出差別。DSLab 那邊記的是 21.2%,我量到 14.5–18.9%,同一個量級。

⚠️ 這一步有個坑會讓你的 1080p 變成 1056p。 _empty_av_latent() 算高度的時候用 height // 16,540 除以 16 是 33.75,無條件捨去變 33,乘回去是 528 不是 540。放大兩倍之後你拿到的是 1920×1056。

解法是在放大之前先插一顆 ImageScale,明確指定 960×540:

MiniMaxH3ImageToVideo (960×540)
  → ImageScale (bilinear, 960×540, crop=disabled)   ← 補這一顆
    → 放大節點 (×2)
      → CreateVideo → SaveVideo

不接這顆不會報錯,你只會在某天量檔案的時候發現高度少了 24 個像素。

開關二:SageAttention 2.2.0,Blackwell 上再省五分之一

SageAttention 是把 attention 這段計算改用量化過的 kernel 跑,換句話說就是拿精度換速度,而在影片模型上那點精度損失看不出來。

5090 上量到 −18.8%(507.0 → 411.8 秒)。同一天我在另一台 GB10 上量到 −20.1%,兩台獨立的機器數字彼此印證。

裝法是抓 2.2.0 的 wheel,然後在 ComfyUI 的啟動參數加一個旗標:

python main.py --use-sage-attention

⚠️ PyPI 上的 sageattention 最新版是 1.0.6,那不是你要的。 2.x 要另外找 wheel 或從 thu-ml/SageAttention 自己編。我一開始裝到 1.0.6,數字完全對不上,查了半天才發現版本不對。

⚠️ 還有一個:--use-sage-attention 只會去找 sageattention 這個套件名。SageAttention 3 是獨立的 sageattn3,ComfyUI 只有註冊沒有自動選用,這個旗標帶不動它。

開關三:放大器換成 RTX VSR,這一刀最深

Real-ESRGAN 的 x2plus 是 2021 年的模型。它在靜態圖上很有名,用在影片上就是一幀一幀跑,跑 362 幀。

NVIDIA 的 Video Super Resolution 是另一套東西:它是為影片設計的,而且跑在 TensorRT 上。Comfy-Org 有官方節點包 Nvidia_RTX_Nodes_ComfyUI,底層就是 NVIDIA 的 nvidia-vfx 套件。

裝完之後多一顆 RTXVideoSuperResolution,接在原本 ImageUpscaleWithModel 的位置。整份 workflow 其他地方都不用動。

放大器整份 workflow只算放大節點
Real-ESRGAN x2plus411.8 s約 109 s
RTX VSR HIGHBITRATE_HIGH314.0 s11.2 s

兩發的取樣段都是 302.8 秒完全相同,差價全部落在放大節點:109.0 秒變成 11.2 秒

「只算放大節點」是怎麼量出來的,方法放在文末的進階那節。

畫質基本上肉眼看不太出來。我抓第 180 幀並排看:VSR 的皮膚細節略多一點,ESRGAN 的背景散景略乾淨一點,兩邊都沒有崩壞,要放大到 200% 才指得出差別在哪。

既然畫質分不出高下,VSR 又快將近一百秒,那就用 VSR。

🔊 開聲音。上下同一個 seed、同一組提示詞、只換放大節點。上面 Real-ESRGAN 411.8 秒,下面 RTX VSR 314.0 秒。

⚠️ 一個很具體的限制:nvidia-vfx 只發 manylinux_2_27_x86_64 的 wheel。Linux 和 Windows 的 x86_64 都能裝,ARM 機器裝不起來,而且這不是「還沒支援」的意思。我另外寫一篇講那條路怎麼走死的。

官方節點的下拉選單裡,沒有你該用的那一檔

節點的品質下拉只有四個選項:LOW / MEDIUM / HIGH / ULTRA

底層的 nvvfx.effects.QualityLevel 實際上有 17 檔:

BICUBIC
LOW / MEDIUM / HIGH / ULTRA
HIGHBITRATE_LOW / _MEDIUM / _HIGH / _ULTRA
DEBLUR_LOW / _MEDIUM / _HIGH / _ULTRA
DENOISE_LOW / _MEDIUM / _HIGH / _ULTRA

這件事很重要,因為官方文件寫得很明白:標準模式是拿壓縮過的實拍影片訓練的,放大的時候會主動去移除壓縮痕跡。

而我們餵給它的是擴散模型剛生出來的畫面——零壓縮痕跡

讓一個專門修壓縮痕跡的模型去修一個沒有壓縮痕跡的畫面,它不會什麼都不做,它會把畫面裡本來就有的紋理當成痕跡去修。結果就是過度銳化或是把細節抹掉。

HIGHBITRATE_* 那一組是為 ProRes、高位元率 H.265 這種乾淨來源設計的,跳過整個 artifact suppression 階段。那才是我們要的。

實拍影片有壓縮痕跡,標準模式清掉它是對的;擴散輸出零痕跡,同一個模式就會把真紋理當成痕跡清掉

要用就得自己把選項補進節點的下拉選單裡。這是節點檔裡一行清單的事,不是什麼大工程。

⚠️ 那 17 檔不是每一檔都該試。DEBLUR_*DENOISE_* 各自在解另一個問題(來源本身糊、來源本身有雜訊),而我們的來源這兩種情況都沒有。

生成解析度的地板:960×540,再低就開始編了

放大既然這麼便宜,直覺的下一步是:那我用更小的畫布生成,再用更大的倍率放大不就好了?

測了。有邊界,而且那個邊界比我想的近。

生成解析度秒數對基準產物大小
960×540 → 2×308.3基準3.4 MB
720×405 → 2.67×181.1−41.3%3.6 MB
480×270 → 4×68.7−77.7%7.5 MB

看最右邊那一欄。480×270 那一列,時間掉了將近八成,而檔案大了兩倍

從更小的畫布放大出來,卻產生更高的位元率——那多出來的高頻資訊不可能來自原始畫面,因為原始畫面裡根本沒有那麼多資訊。是 VSR 補的。

三種生成解析度放大到同樣的 1920×1080:時間一路往下,檔案大小卻在 480×270 反彈到兩倍

畫面上長什麼樣子?720×405 那一檔我的評語是「很勉強」,480×270 是「太糊」。有趣的是它糊的方式跟我預期的不一樣,這點我留到進階那節講。

有一把很好用的尺可以事先估:臉部有效像素。 同一張畫布,遠景構圖的臉大概 170×200 px(34,000 個像素),特寫構圖是 420×540 px(226,800)——差 6.7 倍。所以想要臉的細節,寫特寫比拉解析度有效得多,而且完全免費。

套到上面那張表:480×270 的特寫大約 31,900 個有效像素,跟「不夠用」的遠景構圖同一級。這解釋了為什麼它糊。

想要真 4K?同一條線加 73.9 秒

放大節點支援指定倍率,也支援指定精確的目標尺寸。所以 960×540 → 4× → 3840×2160 是同一條線改一個數字的事。

73.9 秒,產物 16 MB,一樣 362 幀一樣有音軌。

⚠️ 但別忘了上一節。4K 的每一個像素裡,來自原始畫面的比例又更低了。它適合的情境是「來源本來就夠」,不是「用它把不夠的補起來」。

照抄這一組

跑 15 秒 1080p 有聲片,5090 上目前的配方:

生成      960×540 · length 362 · 14 步 · res_multistep + simple · Spectrum 開
校正      ImageScale → 960×540 (bilinear, crop=disabled)
放大      RTXVideoSuperResolution · scale 2.0 · HIGHBITRATE_HIGH
編碼      CreateVideo (fps 24) → SaveVideo (mp4/h264)
啟動      python main.py --use-sage-attention

一支 314 秒。音軌那條線記得直接從 VAEDecodeAudio 接到 CreateVideo.audio——只有影像走放大節點。接錯不會報錯,你會拿到一支沒有聲音的影片。


進階:我怎麼把「放大」這一段單獨量出來

不讀這節不影響使用。這節講的是量測方法跟幾個我判錯的地方,對想自己複製這組數字的人有用。

快取分離法:讓上游整段命中快取

問題是這樣的:整份 workflow 400 秒,放大佔多少?沒人知道,因為它埋在裡面。ComfyUI 不會告訴你每顆節點花了多久。

技巧在於 ComfyUI 的執行快取是照節點輸入算的。只要上游的輸入沒變,重送同一份 workflow,它會直接跳過那些節點。

所以:先跑完整的一發,然後只改放大節點的一個參數再送一次。取樣那一大段整個命中快取,實際重跑的只有放大加編碼。那一發的秒數就是放大那顆節點的成本。

完整跑(冷,含取樣)          314.0 s
只改品質模式重送(熱)          11.2 s   ← 放大加編碼的全部成本

第二發只有放大與編碼兩顆節點真的執行,上游的載入與取樣整段命中快取,所以那一發的秒數就是放大那顆節點的成本

反推 ESRGAN:兩發完整跑差 97.8 秒,而 VSR 那顆節點是 11.2 秒,所以 ESRGAN 那顆約 109 秒

⚠️ 這個推導成立的前提是其餘節點完全相同,所以那 97.8 秒只能來自放大節點。我確認過兩發的 workflow JSON 除了節點 16 以外逐欄相同。

熱跑第一發是 32.4 秒,我猜錯了原因

第一發熱跑不是 11.2 秒,是 32.4 秒

我當下的念頭是:切換品質等級要重新載入 TensorRT engine,所以第一發要付一次載入成本,後面就好了。聽起來很合理。

錯了。

去翻 /history 裡的 execution_cached 清單,三發的內容不一樣:

第一發 (HIGH)        cached = 1,2,3,4,5,6,7,8,9,10,12,15
第二發 (HB_ULTRA)    cached = 1,2,3,4,5,6,7,8,9,10,11,12,15,18
第三發 (ULTRA)       cached = 1,2,3,4,5,6,7,8,9,10,11,12,15,18

第一發少了節點 11 和 18。那是部分快取,不是模型載入。後兩發把 11 和 18 也補進快取,才掉到 11.2 秒。

💡 判快取要看 execution_cached 的節點清單,不要從秒數去推理。秒數會給你一個很合理的故事,而合理的故事不需要是真的。

四個品質模式,一起花 55 秒掃完

有了快取分離法,掃品質模式就變得很便宜:

模式秒數產物大小
HIGH(標準)32.4※3.4 MB ← 最平
HIGHBITRATE_HIGH3.6 MB
HIGHBITRATE_ULTRA11.23.6 MB
ULTRA(標準)11.23.9 MB ← 細節最多

※ 部分快取,見上一節。

模式之間的時間幾乎沒有差別,所以這裡沒有時間跟畫質的取捨,純看畫面挑。

檔案大小在這裡當「高頻含量」的代理指標用:同一組畫面、同樣的編碼參數,檔案越大代表畫面裡的細節越多。而 ULTRA 是四檔裡最大的那個——結合上一節的推理,那 3.9 MB 裡有多少是它補的,就很可疑了。

⚠️ 這個排序不能跨機器搬。 同樣四檔模式我在一張 2080 Ti 上跑,ULTRA 反而是最小的(643 KB vs HIGHBITRATE_ULTRA 的 703 KB),跟 5090 上的觀察完全相反。原因我還沒查清楚,但結論很明確:模式選擇要在你自己的機器上挑。

我的預測錯了一半

看到 480×270 那一列的時候,我寫下的預測是:「臉會塌」。

抽出第 180 幀來看,臉沒有塌。五官的結構、位置、比例全部在,該是眼睛的地方是眼睛。塌掉的是細節——皮膚的紋理變成一片均勻的漸層,頭髮從一絲一絲變成一整塊。

所以那把「臉部有效像素」的尺指對了方向(480×270 的特寫確實跟不夠用的遠景同級),但它沒告訴我壞掉的時候會長什麼樣。像素不夠的結果不是崩壞,是變得平滑而空洞。

這個差別對實務有影響:崩壞你一眼就看得到,平滑而空洞的畫面要並排比較才看得出來。所以如果你打算壓解析度,別只看單支影片覺得「還可以」,一定要跟高一檔的並排。

這條線在別的機器上不成立

最後一件事,免得你照抄了一個對你不成立的結論。

上面每一個秒數都是同一台機器、同一天量的。跨機器的時候會變的東西比你以為的多:

  • VSR 的 x86_64 限制是硬的。 不是效能問題,是那個 wheel 裡根本沒有 ARM 的二進位。
  • 品質模式的排序會翻(見上一節的 2080 Ti)。
  • SageAttention 的加速幅度跟架構有關。5090(sm_120)−18.8%、GB10(sm_121)−20.1%,這兩個接近;更舊的架構是另一個故事,而那個故事我另外寫了一篇。

可以搬走的是比值跟方法:放大那顆節點值不值得量、怎麼把它單獨量出來、HIGHBITRATE 為什麼是對的那一檔。絕對秒數請自己跑。


如果只能記一條,我會留那個快取分離法。

它不只能量放大器——任何一條 ComfyUI 產線裡的任何一段,只要你能只改它的輸入,就能把它單獨量出來。 我調了兩個禮拜的模型,而真正讓它起飛的那一刀,是砍在一個我從來沒量過的小步驟上。

常見問題

RTX Video Super Resolution 可以在 ComfyUI 裡面用嗎?
可以。Comfy-Org 有官方節點 Nvidia_RTX_Nodes_ComfyUI,底層包的是 NVIDIA 的 nvidia-vfx 套件。裝了就多一顆 RTXVideoSuperResolution 節點,接在原本 Real-ESRGAN 的位置就好。限制是那個 wheel 只有 x86_64 版本,ARM 機器裝不起來。
RTX VSR 比 Real-ESRGAN 快多少?
放大影片那顆節點從約 109 秒掉到 11.2 秒,接近十倍。整份 15 秒 1080p 的 workflow 是 411.8 秒對 314.0 秒,省 23.7%。兩發同 seed、只換放大節點、其餘欄位一個都沒動。
RTX VSR 的品質模式該選哪一個?
選 HIGHBITRATE 開頭的那幾檔。標準的 LOW/MEDIUM/HIGH/ULTRA 是拿壓縮過的實拍影片訓練的,放大時會主動去修壓縮痕跡;而擴散模型剛生出來的畫面沒有壓縮痕跡,等於讓它去修不存在的東西。麻煩的是官方節點的下拉選單只給四檔標準模式,HIGHBITRATE 要自己補進去。
低解析度生成再放大,可以低到多少?
在我這條線上地板是 960×540。降到 720×405 畫面已經很勉強;降到 480×270 產物檔案反而從 3.4 MB 漲到 7.5 MB——那多出來的高頻資訊不可能來自原始畫面,是放大器補的。細節變多不等於資訊變多。

接著讀

不想錯過新文章?

訂閱我確保不漏接!

隨時一鍵退訂。