~/blog/dgx-spark-sulphur-nvfp4-video

DGX Spark · part 34

[Benchmark] NVFP4 把影片模型砍小三分之一,速度一模一樣——因為 diffusion 是 compute-bound

cat --toc

TL;DR

DGX Spark(GB10,128GB unified memory)上,NVFP4 把蒸餾版 Sulphur 2(uncensored LTX-2.3)文字生影片模型從 29.2 GB → 19.5 GB(−33%),速度沒變(每支 8.0s,FP8 跟 NVFP4 一樣)、畫質沒掉(銳利,Laplacian 205.79,音訊同步)。這跟 Part 33 剛好相反:那邊 NVFP4 讓 LLM 變快,因為 decode 卡頻寬;這邊影片 diffusion 卡算力,所以 weight-only NVFP4 只把檔變小——速度得靠更猛的 GPU。19.5 GB 這版的意義:現在塞得進 32 GB 的 RTX 5090。已上 HuggingFace

白話版:4-bit 把影片模型變小了,沒變快——而這正是重點

Part 33 裡我把聊天模型壓成 4-bit,它變快了。所以我把同一招用在影片生成模型上——結果速度完全沒動。一樣每支 8 秒,只是檔案小一截。

聽起來像失敗。它不是,而且原因是這整個系列最有用的一個觀念。聊天模型一個字一個字吐的時候,時間花在搬資料——所以資料變小就變快。影片模型的時間花在——而 4-bit 權重在開算之前會被解回正常數字,所以那段算照樣花一樣久。檔小了,時鐘沒動。

那幹嘛還做?因為「小」本身就是獎品。完整模型塞不進一般的遊戲顯卡(RTX 5090,32GB),19.5 GB 這版塞得進。DGX Spark——NVIDIA 的大記憶體桌機——我用它做出小版本;5090 算力大得多,才是它真正能跑快的地方。大記憶體的機器負責壓小,算力猛的機器負責跑快。


前言

Part 33 結尾是 NVFP4 靠速度贏,因為 LLM decode 卡頻寬。很自然會想問:同一招 4-bit,對這台機器整天在做的另一件事——生影片——有沒有用?

硬體跟整個系列一樣:一台 DGX Spark,GB10,128GB unified memory、273 GB/s,ComfyUI 跟 LLM daily 並存著跑。模型是 Sulphur 2,Lightricks LTX-2 影片 DiT(~22B,帶 Gemma-3-12B text encoder + 同步生音訊)的 uncensored fine-tune。蒸餾版我自己量成 NVFP4(沒有現成的),而「有沒有用」的答案很乾脆:沒用——但這個「沒用」很有料。

19.5 GB vs 29.2 GB、8.0s vs 8.0s:NVFP4 影片是省空間,不是省時間

同蒸餾模型、同 8 步 cfg 1.0、同一顆抽出來的 LTX VAE,single-stream 在 GB10 上。唯一的變因是權重格式:

格式檔案大小每支秒數畫質 (Laplacian)
FP8(蒸餾)29.2 GB8.0s銳利
NVFP4(蒸餾)19.5 GB8.0s205.79(銳利)

小三分之一、一秒不差、看不出畫質差、音訊也活著(AAC 48kHz stereo,同步)。把不好聽的講出來:我之前以為 NVFP4 在這 ~28%——那是 N=1、而且比錯了兩個模型(拿 30 步的 dev 比 8 步的蒸餾)。乾淨的 N=3、只換格式,兩邊都 8.0s。NVFP4 在這台對影片只換到一件事:省下 9.7 GB 的硬碟跟記憶體。

同一個 4-bit 格式,為什麼加速了 LLM、卻沒加速影片——roofline

這是兩篇一起讀才有的體會。NVFP4 在這是 weight-only:4-bit 權重被解回 BF16,然後 matmul 用 BF16 算。FP4 tensor core 從頭到尾沒出力。所以 4-bit 唯一的效果,就是少搬幾個 bytes。

  • LLM decode(Part 33)卡頻寬。 batch=1 時 GPU 的時間花在把權重從記憶體搬出來。權重 bytes 砍半,主要成本就跟著砍半——NVFP4 就贏在速度。
  • 影片 diffusion 卡算力。 那 8 步去噪是把算力塞滿的密集 matmul,記憶體搬運不是瓶頸。把 4-bit 解回 BF16,等於把一樣的 BF16 餵給 matmul,時鐘當然不動。

同格式、同機器、相反結果——而且你問一個問題就能事先猜中是哪種:跑這個 workload,是卡在記憶體還是卡在算力?要真的讓影片變快,你得改用 FP4 算力(連 activation 也量化、真的點燃 FP4 核心的 W4A4,那條我還沒做成),或者乾脆給它更多算力單元。這也是為什麼這顆 19.5 GB 比較適合放到 RTX 5090 上跑:影片卡算力,5090 算力遠超 GB10,而 19.5 GB 正好讓它擠得進 32 GB(FP8 模型 29 GB 加上 9 GB 的 text encoder 塞不下)。

那顆會 hang 的 VAE——我差點怪到量化頭上

有一陣子我以為 NVFP4 真的把畫質搞爛了:每支 NVFP4 出來都糊。那是測量假象。NVFP4 checkpoint 的內建 VAE 在 GB10 會 hang——掉進 CPU dequant 然後就卡死——所以我一直把解碼接到 taeltx2_3 這顆 tiny 的 preview VAE。那顆 preview VAE 真的糊:Laplacian ~45,完整 LTX VAE ~205,大概軟了 5 倍。

解法是用完整 VAE 解碼,不要用 tiny 那顆。內建的完整 VAE 對 FP8 沒事、對 NVFP4 會 hang,所以我把它抽成一個 standalone 的 1.45 GB 檔(把 FP8 checkpoint 那些 tensor 的 vae. prefix 剝掉),再用 VAELoader 指向它。這樣一來,同一個透過 tae 看起來糊的 NVFP4 sampling,解出來是 Laplacian 205.79——跟 FP8 沒兩樣。4-bit 權重從來不是問題,preview VAE 才是。

(一個無關的坑:VAE 資料夾裡有顆 ae.safetensors,那是 FLUX 的 VAE 不是 LTX,解出來是垃圾,別照檔名亂抓。)

那個要兩個 ComfyUI flag 才不會炸的 crash

第一次 NVFP4 生成就在 sampler 炸了:

AttributeError: 'NoneType' object has no attribute 'wait_stream'
  in comfy/model_prefetch.py

根本原因:ComfyUI 的 async weight offload(兩條 CUDA stream)加 DynamicVRAM,在 audio+video 一起跑 forward 時對 NVFP4 LTXAV model 解出 device=None,於是 current_stream(None) 回 None。純 video 不會踩到,audio+video 一起跑才會。解法是啟動 ComfyUI 時把這兩個 prefetch 優化都關掉:

python main.py --listen 0.0.0.0 --port 8188 --enable-cors-header \
  --disable-async-offload --disable-dynamic-vram

GB10 有 128 GB unified memory——它本來就不需要把權重 offload 到主記憶體,所以在 GB10 上關掉幾乎沒代價,只是 cold run 峰值略高(LLM daily 還常駐時,cold 那次掉到剩 ~10 GB,離危險還很遠)。

上了 HuggingFace,坑都寫在 model card 裡

量化好的模型在 coolthor/Sulphur-2-distilled-NVFP4——19.5 GB 的 NVFP4 權重、它必須搭配的那顆抽出來的 1.45 GB ltx_full_vae.safetensors、還有 ComfyUI workflow。它沿用上游的 LTX-2 Community License(允許量化後再散布);credit 主要算給 Lightricks(LTX-2)跟 Sulphur fine-tune。model card 一開頭就先講上面那兩個坑,因為一顆會默默解出糊畫面、或卡死在自己 VAE 上的 4-bit checkpoint,比沒有還糟。

收穫

最花時間的地方——控制變因。「NVFP4 快 28% / NVFP4 會糊」這段彎路最花時間,而且兩個都是同一個錯:一次動了兩個變因。又快又糊那次,是 30 步的 dev 模型透過 preview VAE,去比 8 步的蒸餾模型。把模型、步數、VAE 都釘死、只換格式,真相就無聊又正確:同速、同畫質、檔案比較小。

可以直接套用的判斷法——為了速度量化前,先問「在等記憶體還是等算」。 weight-only 4-bit 只在卡頻寬的 workload 上才會變快。LLM decode:會。diffusion sampling:不會。這個 roofline 問題,在你實際跑任何東西之前就能預測結果,而且跟 Part 33 是同一個問題。

通用原則——機器要配瓶頸。 大記憶體的機器(GB10)拿來放、拿來壓模型;算力大的機器(5090)拿來跑卡算力那段。NVFP4 是中間那座橋:它讓 29 GB 的模型塞得進 32 GB 的卡。

結論

如果你在 GB10 上跑 LTX-2 / Sulphur 影片:

  1. NVFP4 weight-only 是省空間(−33%,29.2 → 19.5 GB),不是省時間——影片卡算力,時鐘跟 FP8 一樣。
  2. 完整 LTX VAE 解碼(抽成 standalone 檔——內建那顆在 NVFP4 下會 hang)。永遠別拿 taeltx2_3 當最終解碼器,它是糊的 preview。
  3. ComfyUI 啟動加 --disable-async-offload --disable-dynamic-vram,不然 audio+video 路徑會在 model_prefetch.py 炸。
  4. 蒸餾設定:8 步、cfg 1.0;frame 數 (N×8)+1;audio latent video_frames×4−3
  5. 想要更快而不只是更小?那是 FP4 算力(W4A4)或換大顯卡的問題——這顆 19.5 GB 的存在,就是為了塞進 5090。

常見問題

NVFP4 會讓 DGX Spark 上的影片生成變快嗎?
不會。GB10 DGX Spark 上,蒸餾版 Sulphur 2(LTX-2.3)在 FP8 跟 NVFP4 都是每支短片 8.0s,一模一樣。影片 diffusion 是 compute-bound,而 weight-only NVFP4 算之前會把權重解回 BF16,根本不碰 FP4 核心。賺到的是大小:29.2 GB → 19.5 GB(−33%),畫質不變。
同一台機器,為什麼 NVFP4 能加速 LLM decode、卻不能加速影片?
roofline。single-stream LLM decode 卡頻寬——4-bit 權重只要搬一半 bytes,所以變快。影片 diffusion 卡算力——瓶頸是 matmul 吞吐,而 weight-only NVFP4 解回 BF16 用一樣的方式算,所以你只拿到比較小的檔,不會比較快。
NVFP4 影片模型為什麼會糊?
其實不糊——那是 VAE 的鍋,不是量化。NVFP4 checkpoint 的內建 VAE 在 GB10 會 hang(CPU dequant),所以很容易退去用 tiny 的 tae preview VAE,而 tae 真的糊(Laplacian ~45,完整 VAE ~205)。改用抽出來的完整 LTX VAE,NVFP4 解出來跟 FP8 一樣銳利。
在 DGX Spark 上跑 NVFP4 影片要加什麼 ComfyUI flag?
ComfyUI 啟動要加 --disable-async-offload --disable-dynamic-vram。不然 audio+video 聯合路徑會炸 'NoneType object has no attribute wait_stream'(model_prefetch.py),async prefetch stream 對 NVFP4 model 解出 device=None。GB10 128GB unified memory 本來就不用 offload。