iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0
AI Engineering

GPU很忙?他真的有在做事嗎?系列 第 5

Day 5|一萬顆 kernel 用眼睛看不完,讓 nsys 自己算給你看

  • 分享至 

  • xImage
  •  

昨天你學會了打開行車記錄器,也認得四條列、會讀三件事。恭喜,你已經比大多數只會看 nvidia-smi 的人強了。

但我得先潑你一盆冷水。用眼睛看,撐不了多久。

一份幾秒鐘的訓練 profile,裡面動輒上萬顆 kernel。昨天教你的三個動作(放大、找一圈、收列)能幫你看懂"一個" step,可是當你要比較優化前後、要在八張卡裡找出那張拖後腿的、要一眼判斷"這份到底有沒有生病",光靠眼睛一格一格數,就像有人要你用肉眼點清楚一整座體育場有幾個人。看得到,不代表數得完。

而且這還只是"一份"。真正在調效能的時候,你手上永遠是"兩份",優化前一份、優化後一份,你要回答的是"我這個改動到底有沒有讓它變快、快在哪一段"。兩份各上萬顆 kernel 疊在一起用眼睛比,那不叫工作,那叫上刑。這種又多又重複的比對,本來就不是人腦該做的,丟給機器剛好。

眼睛負責看,但這一季真正要的是量。看得到有洞,跟數得出那些洞總共偷走你幾秒、最肥的是哪顆 kernel,完全是兩回事。用看的,你只能"感覺"哪裡怪怪的;用量的,才講得出一個能寫進報告的數字。今天,就讓 nsys 自己把帳算給你看。

還是同一支 nsys,只是換個吃法

先破除一個可能的誤會,今天你不用再裝新東西。昨天打開 GUI 用的 nsys-ui,跟今天要用的統計功能,是同一套 Nsight Systems,只是一個是圖形介面、一個是命令列。同一支工具,兩種吃法。

Day 4 你用的是它的眼睛,適合你放大、盯著一段慢慢看。今天用的是它的計算機,nsys stats,適合整份一次算完、丟你一張能貼報告的表。想跟著跑,先抓那份 Day 4 用過的公開 profile,再對它下一行 stats:

# 沒抓過的話,先抓這份公開的 8 卡訓練 profile(約 11 MB)
hf download GindaChen/nsys-hero \
  distca-0/baseline.t128k.host-fs-mbz-gpu-899.nsys-rep \
  --repo-type dataset --local-dir .

# 對它算一張 top kernel 表
nsys stats --report cuda_gpu_kern_sum \
  distca-0/baseline.t128k.host-fs-mbz-gpu-899.nsys-rep

它會在背後把 .nsys-rep 轉成 sqlite(跟 Day 4 講的 nsys export 同一回事,你不用自己先轉),跑一個叫 cuda_gpu_kern_sum 的報表,然後直接在終端機印出一張 top kernel 表。不用開 GUI、不用寫 SQL、不用眼睛數。而且它是純文字,適合你 ssh 進遠端那台有卡的機器、連桌面都沒有的時候,一行就有結果。

一行就有的成績單

空講不如直接看一張。那份 8 卡訓練丟給 nsys statscuda_gpu_kern_sum 大概會吐出像這樣的東西(數字是示意,你自己那份一定不一樣):

 Time(%)  Total Time(ns)  Instances   Avg(ns)   Name
  31.2     4,120,553,001     18,240    225,908   ampere_sgemm...  (flash attention)
  14.8     1,955,320,110      6,080    321,600   ncclDevKernel_AllReduce...
   9.1     1,201,880,442     12,160     98,840   elementwise_kernel...
   6.4       845,120,300      6,080    139,000   layernorm_kernel...
   ...

重點不是這幾個數字,是這張表把"上萬顆 kernel 裡誰最肥"一次替你排好了。Time(%) 那欄由大到小,第一名就是你的 80/20 下手處(Day 13)。flash attention 一顆吃掉三成 GPU 時間,那修它最划算,修一顆抵修十顆;AllReduce 佔一成五,是八張卡的通訊稅,留到 Day 18–19 專門處理。

想換一本帳,就換一個報表。看 memory 搬運:

nsys stats --report cuda_gpu_mem_time_sum report.nsys-rep

想看 CPU 側的 API 呼叫、想看你自己下的 NVTX 標記,也各有一個報表(cuda_api_sumnvtx_sum)。nsys 內建的報表有一整排,你要哪本帳就點哪本,全部都是一行 CLI。

把這張表跟昨天對照一下你就懂了。昨天你用眼睛,頂多說得出"嗯,好像有幾顆看起來蠻大的"。今天這張表,直接把"最肥的是誰、吃掉幾成、跑了幾次"變成能寫進 issue、能拿去排優先序的數字。而且它很老實,同一份檔案跑一百次,這張表都一樣,不像你的直覺會因為今天心情好就覺得"還好吧"。

用眼睛看 vs 用 nsys stats 量:左邊放大鏡一次只看得懂一個 step、上萬顆數不完;右邊一行 nsys stats 就把整份的 top kernel 排好給你

掀開蓋子,看這些數字怎麼算

nsys stats 不是魔法,它底下就是把 .nsys-rep 轉成的那顆 sqlite 拿來下 SQL。掀開來看一次,你以後看到任何一個數字,都能自己還原它從哪冒出來。

那顆 sqlite 裡面就是一張一張的表,名字很直白。CUPTI_ACTIVITY_KIND_KERNEL 一列是一顆 kernel,CUPTI_ACTIVITY_KIND_MEMCPY 一列是一次搬運,CUPTI_ACTIVITY_KIND_RUNTIME 一列是一個 CUDA API 呼叫,NVTX_EVENTS 放你的標記。每一列都有 startend 兩個時間戳,單位是奈秒(ns),所以一顆 kernel 花多久,就是 end - start,小學減法。

剛剛那張 top kernel 表怎麼來的?就是對 KERNEL 那張表下一句 SQL,把同名的湊一堆、每顆的 end - start 加總、由大排到小:

SELECT shortName, SUM(end - start) AS total_ns
FROM CUPTI_ACTIVITY_KIND_KERNEL
GROUP BY shortName ORDER BY total_ns DESC LIMIT 10;

一個小陷阱先幫你點破,kernel 的名字不直接存在這張表,表裡存的是一個整數 id,你得再 JOIN 一張 StringIds 表才換得到真正的字串(同一個名字只存一份,省空間)。而且名字有兩種,demangledName 是 C++ 那串又臭又長的全名,shortName 是給人看的短名,你平常看 top kernel 用的是後者。

再來是那條 Day 4 你最愛的線,"誰發射了這顆 kernel"。它底層是一個叫 correlationId 的欄位。每個 CUDA API 呼叫都會被配一個獨一無二的號碼,它發射出去的那顆 kernel 扛的是同一個號。所以 GUI 上那條 CPU 連到 GPU 的線,本質就是拿 RUNTIME 表跟 KERNEL 表用 correlationId 做一次 JOIN,把下訂單的人跟出貨那筆配起來。不神奇,是資料庫的基本功。

最容易算錯的是 idle。你可能以為"把所有 kernel 時間加起來、拿 wall time 一減就是發呆",錯。因為 GPU 上可以有好幾條 stream 同時在跑,兩顆 kernel 的時間會重疊,你直接加會重複計算,算出一個超過 100% 的假忙碌。正確做法是先把所有 kernel 的時間區間聯集起來(重疊的併成一段),得到 GPU 真正被 kernel 蓋住的總時間,wall time 減掉它,剩下的才是真 idle。這種要繞一圈的算法,就不是一句內建報表 cover 得了的,你得自己寫 SQL。這也剛好是那些"疊在 nsys 上面"的工具存在的理由,等一下結尾再說。

再進一步,讓 nsys 自己點問題

到這裡你都還是"自己讀表"。nsys 其實還藏了一招更省事的,nsys analyze。它會拿一組內建的專家規則掃過整份 profile,直接用白話點出常見毛病,連判讀都先幫你做一輪:

nsys analyze distca-0/baseline.t128k.host-fs-mbz-gpu-899.nsys-rep

它一樣在背後轉 sqlite,然後跑一排規則,輸出大概像這樣(示意,你那份不會一模一樣):

CUDA Synchronization APIs (cuda_api_sync)
  cudaDeviceSynchronize 吃掉 CPU 側約 12% 時間,可能在乾等 GPU

CUDA Async Memcpy with Pageable Memory (cuda_memcpy_async)
  用 pageable memory 做非同步 memcpy,會被迫變同步、拖慢搬運

GPU Starvation
  有一段時間 GPU 上沒有任何 kernel 在跑(發呆)

每一條都是一個規則(rule)。cuda_api_sync 抓多餘的同步,cuda_memcpy_async 抓 pageable memory 拖慢搬運,GPU starvation 抓卡在發呆。你發現沒有,這幾條講的正是 Day 2 那三種死法,只是現在變成 nsys 幫你自動貼出來的警示單。

但這裡要踩個煞車。analyze 給你的是"線索",不是"定論"。它說某顆 memcpy 用了 pageable memory,那是事實,但值不值得改、是不是你的主要瓶頸,還是得你回時間軸自己確認。它幫你省的是"該先看哪裡",不是"替你做決定"。

同一支 nsys 幫你讀到哪一層:nsys stats 給你一張表你自己讀、nsys analyze 用規則自動點出問題;再上面那層(nsys-ai,Day 12)才幫你讀完、翻人話、比對

GUI 還是 CLI,哪個才對

都對,而且兩個都會一直用,因為它們是同一支 nsys 的兩隻手。

GUI 適合"看懂一件事的來龍去脈"。你想看某顆 kernel 前後的因果、想親眼確認一個現象,就放大、點來點去、順著那條 CPU 連到 GPU 的線追下去。它給的是理解。

stats CLI 適合"整份量完、給你一張表"。你想比較兩份、想要一組能貼進報告的數字、人在遠端沒有桌面環境,就用它。它給的是總帳。

實務上的節奏通常長這樣,先用 nsys stats 抓大方向(哪顆最肥、通訊佔多少),再切回 GUI 放大那一段、親眼確認它到底怎麼回事。一個告訴你"哪裡有問題",一個讓你看清楚"問題長什麼樣"。兩隻手搭起來,你才又快又不會被工具牽著走。

這些帳,其實也有人幫你自動讀

最後偷偷破個題。上面這些報表,你得自己一個個跑、自己判讀,像 idle 那種還得自己補 SQL。這步其實也能被工具包掉。

我自己有份的一個開源專案 nsys-ai,就是疊在 nsys 上面那一層,幫你把這些報表一次跑齊、把 idle 這種眉角算好,再翻成人話。它的 0.3.0 快發了,會帶上 MCP server、Claude plugin,還有一個比原廠 GUI 更順的 web timeline。今天先破題就好,不細講,這種"上層工具"我們留到 Day 12 專門玩一整天。有興趣先逛:GindaChen/nsys-ai。業配歸業配,這句是真心話,但今天的主角始終是 nsys 本身,先把地基踩穩再說。

小結與明天

今天你發現,看不完的時候不必硬用眼睛數,同一支 nsys 的 CLI 統計一行就把帳算給你。你也掀開蓋子看過,那些數字底下就是一顆 sqlite 加幾句 SQL,不是魔法。眼睛用來理解結構,nsys stats 用來算出總帳,這兩件事今天都學會了。

但前面兩天我們都在讀"別人給的"那份現成錄影。真正上工的時候,那份錄影得你自己錄。明天 Day 6,我們就來學這一季第一條真正要背起來的指令,nsys profile,怎麼用最小的設定,把你自己那支訓練 job 的那幾秒錄下來。

眼睛會看了,帳也會算了。明天,我們學會自己按下那顆錄影鍵。

參考資料


上一篇
Day 4|別急著開Agent叫他幫你看,先學會看懂一條時間軸
系列文
GPU很忙?他真的有在做事嗎?5
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言