![]()
今天聊點新東西——Unsloth 和 NVIDIA 官方聯手了,三個看起來很"無聊"的優化,組合起來直接把 LLM 訓練速度提了大約 25%
而且最騷的是:你不用換硬件、不用改代碼、不用換框架,升級一下 Unsloth 就能吃到紅利
這次到底動了什么
通常我們說"訓練加速",第一反應是更快的 matmul、更好的 attention、fused kernel、grouped GEMM 這些大頭算子
但 Unsloth 團隊和 NVIDIA 工程師坐下來一起 profile 的時候,發現一個很反直覺的事實——那些大算子早就被卷到極致了,真正在拖后腿的,是算子之間的膠水代碼
具體表現是兩類:
GPU 在等"元數據"——一些只跟 batch 相關的小信息,每一層都重新構建一遍
拷貝流和計算流被排成了一隊——明明可以并行,卻輪流執行
針對這兩類問題,他們在 Blackwell GPU 上做了三處改動:
Packed-sequence 元數據緩存 :PR unsloth#4243
雙緩沖的 checkpoint 激活回傳 :PR unsloth-zoo#534
GPT-OSS MoE 路由用 bincount 一次分組 :PR unsloth-zoo#535
三個 PR 看著不起眼,但每一個的視角都挺值得學的
![]()
三個優化各打一處,組合 ~25% 優化一:別把同一份元數據重建 L 遍
先說一個微調里很常見的小技巧——packed sequence
短樣本一堆,如果按最長那個 padding 對齊,padding token 上浪費的算力不少。所以大家都改成把多條短樣本拼成一條長 sequence,再用一份"邊界元數據"告訴模型每條原樣本從哪到哪
這份邊界元數據 B 包括:
每條樣本的長度
累積偏移
cu_seqlens最大序列長度
由上面三項推導出來的 attention mask 結構
關鍵來了:對一個 packed batch 而言,這份 B 在所有 transformer 層里是一模一樣的
但很多實現的寫法是:每一層走 attention 的時候,都重新算一遍 B、重新構建一遍 SDPA 的 packed mask、重新生成 xFormers 的 block mask
如果模型有 L 層,本來"build B 一次,用 L 次"的事情,被寫成了"build B + build B + ? + build B",重復 L 次
更糟糕的是,這條路徑里有些調用會強制 device-to-host 同步,每一層都觸發一次 GPU-CPU 同步點
Unsloth 的改法很直接——給當前 packed batch 緩存一份元數據和 mask 結構,按設備分別緩存,后續所有層直接復用
數學上能直接算出來收益:假設原來每層附帶的同步/重建開銷是 s,緩存之后省下來的時間大概是 (L ? 1) · s
實測在 Qwen3-14B QLoRA SFT 上:
前向:+43.3%
反向:+5.8%
整個 batch:+14.3%
前向受益最大,因為元數據和 mask 準備主要發生在前向。反向因為本身計算量更大(尤其疊加 gradient checkpointing),同樣的絕對節省時間,相對比例就小一些
為了驗證這個數字合理,團隊還在 Blackwell 上做了微基準——一次 packed SDPA mask rebuild 大約 13.7 ms。按 (L ? 1) · m 估:
Llama-3.2-1B,16 層:每步省 199 ms,端到端約快 11.5%Qwen3-0.6B,28 層:每步省 319 ms,端到端約快 14.8%
理論值和實測值對得上,這種"先優化、再用模型解釋清楚為什么有效"的思路,比單純丟一句"我們快了多少"靠譜得多
優化二:雙緩沖,讓拷貝藏在計算后面
第二個優化關系到 gradient checkpointing
這個技術微調老手都熟——為了省 VRAM,前向時不保留所有中間激活,反向時再重新算一遍。但 Unsloth 走得更激進,開了 smart checkpointing:把激活直接挪到 pinned CPU memory,反向時再傳回 GPU
省顯存是省了,但多了一條慢路徑:
1. 把激活從 CPU 拷回 GPU
2. 等拷貝完成
3. 在這塊激活上跑反向
4. 開始下一份激活的拷貝
如果只用一個 buffer,拷貝流和計算流就會輪流堵車——拷貝的時候計算閑著,計算的時候拷貝閑著
解法老到不能再老:雙緩沖
A buffer 在跑反向計算的時候,拷貝流就把下一層的激活預加載到 B buffer,反過來再換。中間層的拷貝就被藏在了反向計算后面
![]()
單緩沖 vs 雙緩沖:拷貝藏到計算后面
注意這個改動沒有減少任何運算,它只是把"原本被串行的工作"變成了"重疊的工作"
理論上每層的中間耗時從 c + g 降到 max(c, g)。第一次拷貝和最后一次計算還是要曝露出來,所以端到端節省是 (L ? 1) · min(c, g)
實測在 NVIDIA B200 Blackwell 上(dense 模型):
模型規模
單步速度變化
提速
多用 VRAM
8B
0.3739 → 0.4053 steps/s
+8.40%
+0.37 GB
14B
0.2245 → 0.2395 steps/s
+6.70%
+0.47 GB
32B
0.1979 → 0.2070 steps/s
+4.61%
+0.23 GB
而且 final loss 基本沒變
注意到一個細節:模型越大,絕對節省時間越多,但相對加速反而下降——因為反向計算量本身就大了,能藏的時間相對沒那么顯眼
這里 Unsloth 留了幾個工程細節做兜底,挺值得學:
VRAM 夠才開第二個 buffer
顯存吃緊自動 fallback 回單 buffer
數值結果保持一致
也就是說:你的卡顯存大就吃滿紅利,顯存小也不會因為這個改動 OOM
優化三:MoE 路由的"分組一次性做好"
第三個優化更冷門一點,針對 GPT-OSS 的 MoE 路由
PyTorch 實現里有個常見寫法:
for expert_idx in range(num_experts):
token_idx, _ = torch.where(router_indices == expert_idx)
看起來人畜無害,但 torch.where 在這里是 data-dependent——每個 expert 拿到多少 token 是動態的,輸出 shape 不確定,會觸發 CPU-GPU 同步或者類似的 runtime 開銷
更糟的是:這個開銷一個 expert 一次,規模隨 num_experts 線性增長
Unsloth 的改法是把這事一次性做完:
把所有 expert 分配 flatten
按 expert ID 做 stable sort
用一次
bincount拿到每個 expert 拿到多少 token從 count 推導 offset
直接按 offset 切 token list
邏輯沒變,但對 runtime 的"動態查詢次數"從 num_experts 直接降到 1
效果:
GPT-OSS 端到端:**~10–15%** 提速
路由熱點路徑:前向 **+23%**,反向 +13%
注意一個邊界條件:這只在 native_torch 后端的 MoE 上生效,用 Megablocks/grouped GEMM 那一套的不歸這個改動管
三個優化合在一起:~25%
單看每個 PR:
優化
主要消除的瓶頸
實測收益
Packed-sequence metadata caching
跨層重復構建/同步元數據
Qwen3-14B QLoRA SFT:+14.3% per batch
Double-buffered checkpoint reload
拷貝和反向計算串行
8B +8.4% / 14B +6.7% / 32B +4.6%
GPT-OSS MoE routing with bincount
per-expert 動態索引同步
GPT-OSS 整體 ~10–15%
組合起來,官方給的口徑是 "~25% faster"
? 這里說一句實話——25% 不是疊加得到的精確數字。三個優化作用面不完全重疊(QLoRA SFT、checkpoint 路徑、MoE 路由),具體多少跟你訓什么模型、用什么后端、卡多大顯存都強相關你能用上嗎?怎么開
這是我最想說的一段——對家用 GPU 用戶尤其友好
這三個優化的觸發條件是你本來就在用的常見配置:
Packed-sequence caching:用了
packing=True的 SFT/QLoRA 都會自動吃到,是 Unsloth 默認推薦的做法雙緩沖 checkpoint:用了 Unsloth smart gradient checkpointing 而且 VRAM 還有富余,自動啟用
MoE routing:訓 GPT-OSS 系且走
native_torch后端,自動啟用
啟用方式只有一行:
pip install --upgrade unsloth unsloth_zoo
這事的價值在于:以前這種"和廠商一起 profile 出來的內核優化",基本都長在 Megatron-LM、NeMo、TRT-LLM 這種偏數據中心的棧里。家用 4090、5090、48GB 的工作站基本指望不上
現在 Unsloth 把它做進了一個 pip install 就能裝的庫里,你拿一張 RTX 4090 跑 Qwen3-14B 的 QLoRA,吃到的優化和數據中心 B200 上是同一份代碼
? 當然 24GB 卡跑不動 14B 全參,但 QLoRA + packing 是吃得到的;如果你有兩張 4090 / 一張 5090 / 一張 6000 Ada / DGX Spark 這種 48GB 級別的卡,受益會更明顯和 HuggingFace TRL/PEFT 比
很多人會問:HuggingFace 的 TRL + PEFT 不是也能做 QLoRA 嗎,區別在哪?
TRL/PEFT 的定位是通用、穩定、覆蓋廣——支持各種模型、各種任務、各種后端,但不會專門為某個后端做深度 profile
Unsloth 的定位完全不同——只支持一部分主流模型,但每個支持的模型路徑都被手擼過 kernel、profile 過同步點。這次和 NVIDIA 的合作,本質上是把數據中心級別的優化思路下放到家用 GPU 這一檔
實際選擇建議:
想在家用 GPU 上把單卡微調跑到極限,選 Unsloth
想兼顧多卡分布式、奇怪的模型、復雜的 RLHF pipeline,TRL + DeepSpeed/FSDP 更順手
不沖突,可以 Unsloth 做單卡微調原型,TRL 做大規模訓練
其實讀完這三個 PR,最大的感受不是"提速 25% 真香",而是這種工程方法學的味道——
"內核之外,讓該并行的東西真的并行;讓該緩存的信息真的只算一次"
這背后是一句很樸素的話:當主算子被卷到極致后,"快"只剩兩條路——少做沒必要的事,把沒法避免的事并行化
這種思路放到推理優化、Agent 框架、RAG 流程里都成立。下次你看自己的 pipeline 慢的時候,先別急著換模型,profile 一下"重復構建的元數據"和"被串行的拷貝/計算",往往才是真正的大頭
制作不易,如果這篇文章覺得對你有用,可否點個關注。給我個三連擊:點贊、轉發和在看。若可以再給我加個,謝謝你看我的文章,我們下篇再見!
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.