為了讓這類專用推論引擎能移植到RDNA家族,這是我啃了它十五個 kernel 後。gufo 快不是一個絕招,是十層疊起來的「零浪費」哲學:
讓它快的關鍵(按影響排序)
1. Dequant 永遠不落地(最大的一塊)
權重以 int8 進 WMMA,scale 只在最後乘一次——fp16 權重從不存在於記憶體。llama.cpp 系是先解量化成 fp16 再算(頻寬翻 2~4 倍)。這就是為什麼 Q3_K_M 跑出 310GB/s 有效頻寬:它真的只讀了 13GB。
2. 什麼都 fuse
RMSNorm+量化 activation:一遍搞定(FusedRMSNormQuantizeQ8_1)
SwiGLU:gate+up+激活函數一個 kernel(BlockedSwiGLUFp32)
Attention:QK^T+softmax(online 跑 max/sum)+P×V 全在 LDS 裡轉完,S 矩陣不出片
每 fuse 掉一次就省一整輪 VRAM 讀寫——decode 這種 memory-bound 場景直接變速度
3. 一個形狀一種 kernel,不做通用
small batch、prefill、wave64 是三套獨立 kernel(small_batch_wave64、prefill_quant、w8a8),不是一個 kernel 加 if。這就是它目錄爆炸的原因,也是它快的原因——每個 kernel 的 tile、occupancy、swizzle 都是為那個形狀手調的
全 constexpr+static_assert:head_dim、GQA 數全是編譯期常數,runtime 零分支
4. WMMA 玩到極致
hand-written wave32/wave64 fragment,LDS swizzle 避 bank conflict(LoadSwizzled 那套),launch bounds 壓 occupancy——我在移植時看到的每個常數都是調過的
整數 dot 走 wmma.i32(Q8 累加 32 位,精度還比 fp16 好)
5. 零框架稅
沒 torch、沒 ggml graph dispatch——C++ 直接 launch kernel,權重一次 hipMalloc 常駐,scratch 重用,session 預分配。llama.cpp 的 graph 開銷在它這裡是零。
6. 推測解碼雙保險(qfn 有 ngram.cpp+MTP):n-gram Lookup+小模型 draft 兩路並行,decode 少跑好幾次大模型。
7. Strix 紅利(不可移植):gfx1151 調校+128GB 統一記憶體零拷貝——這部分是作弊,換硬體就沒了。
哪些概念能搬去別的引擎
概念
可搬性
說明
dequant-in-register(永不落地)
任何 memory-bound 引擎吃了都快,llama.cpp 最該抄
norm+quant+GEMM fuse
純工程活,無硬體依賴
online softmax+S 不出片
FlashAttention 已在做,gufo 版更激進
形狀特化 kernel(小 batch 獨立路徑)
要維護成本,llama.cpp 的 mmpq 方向類似但沒這麼絕
speculative 雙 draft
n-gram 表那套很便宜,人人可抄
WMMA fragment 手調
綁架構(我現在手上有 gfx1151+gfx1201 兩套 mapping,要的拿去)
統一記憶體假設
別學,會死(問我怎麼知道的)
一句話總結
gufo 的哲學是:把「通用」當成敵人——通用格式、通用 kernel、通用框架,每個通用都收稅;它一個個手寫收回來。這套在它能覆蓋的模型上無敵,代價就是新模型新架構要重寫一遍。