iT邦幫忙

2026 iThome 鐵人賽

DAY 30
0
Software Development

GPU 效能優化實戰:30 天從 Kernel 到 Profiling (重賽版)系列 第 30 篇

# Day 30:所以,這 29 天到底優化了什麼? (重賽版)

  • 分享至 

  • xImage
  •  

(也是完賽板拉 嘿嘿 )
短短的總結一下:

這29天 以學 GPU 優化來看,可以切成四段。每段學的不是「又一個題目」,而是不同層的技術。

1–3 怎麼量、GPU 怎麼執行、tiling
4–10 改數據表示,把問題送進 INT8 Tensor Core
11–14 融合、profile、更高精度
15–20 記憶體層級上的資料交換,以及 Tensor Core 的界線
21–29 演算法少算、控制留 device、結論不要講過頭

剩下的 更多是個人感言,如果有緣 我可能還會再發正式版
先謝謝有看到今天ㄉ人拉~

Namaste,ありがとう,Thank you,謝謝,감사합니다,多謝,Danke。

那麼 就讓我們開始 GPU 重賽版day30的總結吧:


Day 1 的時候,我寫過一句:

沒有 Baseline 的 100 ms,不是快,也不是慢。它就只是 100 ms。

寫到 Day 30,我發現這句話也可以拿來問自己。

「我連續寫了 30 天」聽起來好像很厲害,但 30 天本身不代表文章寫得好,也不代表我已經很懂 GPU。它只代表我確實交出了 30 篇東西(或是AI交了30篇 哈哈哈)。至於裡面哪些地方有講清楚、哪些地方做對了、哪些只是當時以為自己做對了,現在還是得回頭檢查一次。

所以今天不介紹新的 Kernel,也不再塞一個新名詞進來。這篇就是心得兼檢討,回頭看看這 29 天到底寫了什麼、哪些地方有問題,以及我從裡面學到了什麼。
(如果要看技術部分的總結 可以跳蛋這部分: 這 29 天真正反覆碰到的,其實就三件事)


這 29 天真正反覆碰到的,其實就三件事

如果硬要把四條線收成同一個主題,真正成立的不是「教完所有 CUDA 技巧」,而是這三句:

第一,問題本身的結構,往往比熱門硬體功能還重要。

GEMM 能把 Ozaki / CRT 的固定成本攤進大量乘法裡,所以 Tensor Core 這條路至少看起來合理。FWT 付不起 transpose 和 layout 轉換,硬塞 MMA 反而更慢。LCN 最早比較明顯的收益,也不是先用什麼很炫的指令,而是少算那些移動一個點之後根本不會變的 edge pair。

同一顆 Tensor Core,在三個題目上能發揮的作用完全不一樣。

第二,瓶頸會跟著優化一起改變位置。

Tiling 之後,瓶頸可能從 global memory 跑去 register pressure 或 epilogue。Crossing 從 full recount 改成 incremental 之後,下一個問題變成 host 每一步都在等 GPU。把 SA 留在 device 上之後,你量到的就不再只是 kernel 快了多少,而是搜尋過程有沒有被改掉。

所以「這顆 kernel 變快了」常常只是中間結果,不是最後結論。

第三,效能結論必須有足夠的根據,才能這樣講。

Baseline、correctness、precision、timing scope、work unit、outcome,少一項,都不能把「某段變快」寫成「系統變快」。Day 29 對 1.96× 的限制,其實就是把 Day 1 那句話再講一次,只是這次講得更嚴格。

寫文章其實也是一樣。

篇數不能代表成果,字數不能代表效率,AI 幫我生出更多內容,也不等於讀者更容易理解。最後還是要回到那個最麻煩、也最重要的問題:我到底有沒有把事情講對、講清楚?
(後來我把這段移到前面 這樣mur mur 以及抱怨就都在後面了)

一開始真的想太大了

最早決定寫這個系列時,我想的是 GPU optimization。我想把做過的實驗整理成文章,讓自己的專案和 PR 有一份比較完整的說明,也想證明自己不是把程式碼丟上去就算了,而是真的知道它為什麼快、為什麼慢。剛好看到大家都在寫技術文章,就一股腦衝了進來。

現在回頭看,我一開始根本沒有把範圍想清楚,路線也沒有先規劃好。

Day 1–3     什麼是優化、GPU 是什麼、Tiled GEMM
Day 4–10    高精度 GEMM:Ozaki、CRT、INT8 Tensor Core
Day 10–14   即興支線:補精度、DoubleDouble、FP128
Day 15–20   換題。Hadamard / FWT,想把 Tensor Core 塞進去
Day 21–29   再換題。LCN:crossing、layout、SA

這不是一條規劃完整的 30 天路線。比較像是我隨便把四個自己做過的題目拿來當主題,而不是先挑選過、確認它們適不適合放在同一個系列裡。

Day 1 到 Day 3 我還寫得比較認真。Day 4 以後就開始難掌控了。Day 10 到 Day 14 是臨時即興的支線;Day 15 到 Day 20 是我想做的一個 PR,想講怎麼用 Tensor Core 去模擬 FWT,最後失敗了。Day 21 到 Day 29 又換成完全不一樣的大主題,裡面還有三個小子題。

單看其中一些文章,材料其實不差。但從頭看到尾,讀者會覺得很跳:才剛看完高精度 GEMM,下一篇忽然變成 FWT;好不容易知道 Hadamard 在幹嘛,又被抓去數圖上的 crossing。

我想講的東西很多,卻沒有先決定讀者到底要跟著看哪一條。這是整季最大的問題之一。

如果之後有正式重寫,我大概不會再把它硬拆成 30 篇。高精度 GEMM、FWT 和 LCN 各自濃縮成幾篇完整的 case study,應該比每天追著日期跑更適合。Day 10 之後我本來還應該繼續發正式版,但那時候累了、懶了,也把原本開賽團隊的進度搞砸過一次,後來就自己再發一個重賽版。正式版還會不會發呢?不一定。就算發了,可能也不會再是 30 篇。


自己的文章 自己罵

更麻煩的是,有些地方不只是難讀,而是真的不夠嚴謹

如果只是文章太長、笑話不好笑,頂多算寫作問題。技術文章真正不能混過去的,是數字、定義和結論。

Day 1 才說沒有 Baseline 就不能談優化。Day 3 花了很多篇幅教怎麼比較 naive GEMM 和 tiled GEMM,最後卻沒有把完整的實驗結果補回來。圖沒有,數字也沒有。自己先把第一天講過的原則忘了。

Day 4 的 RTX 4060 算力數字也有問題。我把接近單一 SM 的估算,寫得像是整張卡的規格。方向沒有錯:消費級 Ada 的 FP64 確實很弱,64:1 的比例也對。但方向正確,不代表絕對值可以寫錯。這種錯誤很危險,因為它很容易被一個看起來合理的故事蓋過去。後來雖然有重賽版,可是連載當時發出去的,還是錯的那份。

Ozaki 和 CRT 那一段的問題更大。我一開始想處理的是 FP64,實作和文章卻沒有從頭到尾守住同一份 precision contract。某些 fast path 實際上比較接近 34-bit mantissa;有些驗證路徑又偷偷借用了 FP64。表示範圍、切片位數和重建條件,也沒有一開始就講清楚。

更尷尬的是,有些 code 我那幾天才真正拿出來看。然後才發現,我算的是 FP64,卻讓 AI 在驗證的地方插了一個 FP64 進去精算。那一步我本來就不該需要。為什麼會需要?因為當時精度設定不對、位元解析度也不對。答案跑得出來,誤差看起來也不大,於是文章就繼續寫下去了。

這是我這次最痛的一課:

答案跑得出來、誤差看起來不大,跟演算法真的滿足原本承諾,是三件不同的事。

Hadamard 那段也有類似問題,但性質不一樣。Tensor Core 聽起來很帥,我也確實很想把 FWT 塞進去,但硬體很擅長 matrix multiply,不代表每一個能寫成矩陣的東西都適合丟給它。資料搬移、padding、同步和轉換的成本加進來之後,原本看起來很漂亮的想法就不一定划算。某些比較裡的數值誤差,也大到不該只用一句「大概可以」帶過。

反而是這次失敗,讓 Day 15 到 Day 20 成為我覺得相對完整的一段。它不是「我成功用 Tensor Core 打爆 FWT」,而是從嘗試、碰壁,到重新切問題,最後才理解為什麼這條路不適合。PR 沒有照原本期待成功,文章也沒有達到那位推薦我寫 blog 的同學的水準,但失敗的過程是真的。這樣至少有收穫,也算有成長。

所以這 29 天裡,其實有三種完全不同的結果:

高精度 GEMM
    做出來了,但不能用原本的方式宣稱它是完整 FP64

FWT + Tensor Core
    這條路沒有比較快。低 arithmetic intensity 的 operator
    不該為了 MMA 再多付一筆 layout 和 transpose 的成本

LCN crossing
    Kernel 在特定條件下快了 1.96 倍
    但那只是 evaluator,不是整個 solver

這三件事不能混在一起,講成同一種成功。Day 12 的 prototype、Day 18 的失敗實驗、Day 29 的 1.96×,回答的根本不是同一個問題。


後面寫得比較好了嗎?有吧? 但真的太多了....

Day 21 到 Day 29 是另一個極端。

我開始比較認真地定義問題:什麼叫合法 layout、crossing 怎麼算、incremental update 和 full recount 是否逐 edge 相同、Kernel time 和 solver outcome 能不能混在一起。到了 Day 29,我甚至很龜毛地問:Kernel 快了 1.96 倍,為什麼不能說 Solver 也快了 1.96 倍?

這套標準才是我希望前面也能做到的:先定下正確的答案、先立下正確的 baseline,證明改寫前後語意一致之後,我們再去量真正被改動的那一層。

可是後面也真的太肥了,文章變得很長。

能把 Day 21 到 Day 29 全部看完的人,我真的要給他四個大拇指。裡面同時有數學定義、GPU memory hierarchy、Kernel 重寫、搜尋演算法和 benchmark methodology。有時候一篇文章其實塞了三篇的內容。我自己做專案時,常常只需要確認公式、硬體效率和方向,接著就能繼續迭代。但讀者沒有參與我的前情提要,也不知道 repo 裡那些 Phase22、C1、Flash-SA 是從哪裡冒出來的。

把內部開發紀錄直接搬成教材,對我來說很順,對讀者來說卻像闖進別人開到一半的會議室。東西多不叫完整,而是要精簡到讓人看得懂。有時候只是作者捨不得刪。

Day 4 以後,我也越來越難篇篇自己寫完。如果要全部親力親為,又很容易跑出太多情緒文,像 Day 8 就有太多沒必要的抱怨。後來我盡量把這些移到後面,避免影響技術內容。但技術要寫清楚、同時又不要太沉悶,中間真的很難拿捏。沉悶的時候也不能隨便塞一個自以為幽默的梗。Day 18 開頭那一大段迷因,事後看就是把另一篇文章塞進技術文裡。

寫技術文確實很難。難的是節奏要對、內容要可信,也難在你得承認自己有些地方其實沒講清楚。


技術文不是在證明自己什麼都懂

前面幾天,我大多是自己寫完就發,沒有認真找人審稿。後來我開始自己重看,也讓 AI 幫忙挑問題、double check 和 review。

AI 確實抓到了不少我沒看到的東西。像是證據沒接上結論、讀者定位一直變、程式碼沒有附、名詞在不同篇代表不同東西,還有該深入的地方一句帶過、不需要展開的地方卻寫了一大串。這些批評大部分都是對的。Flash 這個詞在系列裡至少被我拿去指過三件不同的事:FlashAttention 的精神、小圖的 on-chip backend、以及 device-resident SA。讀者沒有義務幫我把這三個名字拆開。

但 AI 也很容易把三、四百字可以講完的內容灌成一、兩萬字,寫出一份看起來完整、讀完卻記不得重點的東西。更重要的是,AI 可以幫忙找漏洞,不能替作者承擔技術結論。最後決定一個數字能不能寫、一個 speedup 能不能宣稱、一個方法到底算不算 FP64 的人,還是我。

我以前不太敢把東西直接丟給真人看。可能是害羞,也可能是怕別人一問,我就發現自己其實沒有想清楚。我沒有去問真人對這些文章的感想,一部分是因為對方如果不懂 GPU、不喜歡看也不想寫,去問只是浪費人家時間。但把文章公開,本來就是接受 review。提問和請人檢查沒有那麼可怕,最糟大概就是被拒絕,或者被指出真的有錯。後者雖然痛,還是比錯誤一直留在文章裡好。

下一次如果再整理這些內容,我想先守住幾個很普通、但這次常常沒做到的規則:

  1. 一篇只回答一個主要問題,先說這篇是寫給誰看的。
  2. 所有效能數字都交代硬體、輸入、baseline、計時範圍和排除項目。
  3. 所有精度宣稱都附上明確的 precision contract 和誤差結果。
  4. 程式、原始結果和 reference 跟文章一起整理,不要等寫完才補。
  5. 發布前至少找一個不是我自己的人看過。

本來我只想講幾個 GPU 主題,甚至讓 AI 生成簡單內容、貼貼教科書就好,這樣比較省事也不容易出錯。搬運本身不是壞事,重點是要有 reference、並且講清楚。後來發現不能純粹搬運,純搬運會變得不知所謂。事情排太滿之後,不知道怎麼寫,就開始把它當成個人部落格在寫了。

這也不全是壞事。但 side hustle 拿出來之後,還是得被檢驗。喜歡寫、能讓自己開心,跟內容能不能站住,是兩件不同的事。


所以這 30 天算成功嗎?

老實說,我碰 GPU 的時間並不長。

大概一年前,我才真的開始上 GPU 相關的課;再早一點,我甚至不知道自己會去學 parallel computing。大學的時候我對硬體完全沒興趣,覺得自己走軟體就好,軟體可以發大財。結果現在軟體好像要被 AI 取代了,只好趕快滾來硬體這邊取暖。至於硬體會不會收留我,那又是另一個問題了。

知道 Ozaki 之後,我只是覺得這棵技能樹好像很硬,值得點下去看看。後來去碰 contest、寫 Kernel、做 PR,也常常懷疑自己是不是一個想擠進窄門的半吊子。我不熟寫作,程式也還有很多不熟,GPU 更不用說。三個都不熟,聽起來實在很不妙。

可是如果因為不熟就不做,那就永遠不會熟。

去面試的時候,人家會問你熟不熟。你不熟就找不到工作;但你沒有工作,又怎麼會熟悉?直接掉進死循環。所以哪怕不熟,我還是得做;哪怕沒有時間,還是得擠出時間來做。時間有時候跟牙膏一樣,擠一擠就會有。至於這些時間最後算不算浪費,我其實也不知道。我只知道,如果你連擠都不擠,後面那些問題根本不會發生,當然也不會被你解掉。

你說這樣不會感到快樂嗎?快樂啊。

當 profile 裡的瓶頸和自己的推論對上,當原本卡在 CPU / GPU 之間來回的狀態終於能留在 device 上,當一個很慢的 baseline 被一步一步改掉,那種快樂是真的。它不會因為別人做得更快、更深,或我的文章寫得不夠好就消失。

所以,如果要替這 30 天下結論,我不會說自己已經教會大家 GPU optimization。這個說法太大,我沒有足夠的證據。

我比較敢說的是:我把幾個不成熟的想法真的做了出來,也把其中一些錯誤、失敗和修正攤在大家面前。我開始知道,優化不只是讓某顆 Kernel 的數字變漂亮,而是要先問:

我到底承諾要算什麼?
現在真正慢在哪裡?
改完還是同一個問題嗎?
我量到的數字能支持多大的結論?

目前的答案大概是:有幾篇有,有幾篇沒有;有些地方比我想像中好,也有些地方需要正式重寫。

至少現在,我知道該從哪裡改了。


最後,謝謝所有願意讀到這裡的人。不管你是從 Day 1 一路看到 Day 30,還是今天第一次點進來;不管你真的看完了所有公式,還是滑到一半只想知道結論,都謝謝你願意把時間花在這些文字上。

我不知道從第一天看到最後一天的人有幾個。老實說能把後面那九天看完的人 或是能看完任何例子的人 (或是看了我的一堆mur mur的人 這個也是超感謝 哈哈哈哈),真的 比我還棒。我聽到很多前輩說過 開源不只有寫 code 是貢獻,願意找問題 提測試和 review 別人的東西也很重要。我很榮幸這些還不成熟的內容曾經被你們看過,也希望下一次拿出來時,它們會更值得您們目光。

Namaste,ありがとう,Thank you,謝謝,감사합니다,多謝,Danke。

總之 先完結拉~
Ciao~


您已閱讀 7 0 4 6 字


上一篇
Day 29:Kernel 快了 1.96 倍,為什麼不能說 Solver 也快了 1.96 倍?「重賽版」
系列文
GPU 效能優化實戰:30 天從 Kernel 到 Profiling (重賽版) 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言