iT邦幫忙

2026 iThome 鐵人賽

DAY 6
1
AI Engineering

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

Day 6|自己按下錄影鍵,紀錄運行的過程

  • 分享至 

  • xImage
  •  

Day 4 跟 Day 5,你一直在看"別人給的"那份錄影,用眼睛讀、用工具量。今天,我們把攝影機轉過來,對準你自己那支 job。

裝行車記錄器不難,難的是你按下錄影鍵之前,得先知道要錄什麼、錄多久。按錯了,你會得到一個幾十 GB、打都打不開的怪物,或者一份被錄影本身拖慢、量出來的數字根本不準的廢檔。今天的主題只有一句話,錄最少能回答問題的東西,不要錄最多

一行就開錄

先給你那行指令,其實比你想的短:

nsys profile --trace=cuda,nvtx -o my_profile python train.py

就這樣。把你本來 python train.py 的跑法,前面加一句 nsys profile,後面它就會幫你把整段跑起來、同時錄下來,結束後吐出一個 my_profile.nsys-rep,正是 Day 4 你用 nsys-ui 打開、Day 5 你用 nsys stats 算帳的那種檔。

拆一下這幾個字:

  • nsys profile:啟動 Nsight Systems 的錄影模式。
  • --trace=cuda,nvtx:只追蹤兩個東西,CUDA 跟 NVTX。這是今天的重點,等下細講。
  • -o my_profile:輸出檔名,會存成 my_profile.nsys-rep
  • python train.py:你原本要跑的命令,原封不動接在最後面。

不用改一行程式碼,不用裝什麼奇怪的 wrapper。而且它不挑,後面接的不一定要是 python,換成你的推論服務、一個 C++ binary、任何會用到 GPU 的命令都行,nsys profile 只負責把那段時間裡的 GPU 活動錄下來。這也是為什麼 nsys 是這一季的主線,它的門檻低到你今天就能用在自己的 job 上。

為什麼只錄 cuda 跟 nvtx

--trace 後面能填的東西很多,cudanvtxosrt(作業系統呼叫)、cudnncublasncclmpi… 一長串。新手的直覺是全部都開,反正資訊越多越好。這是第一個要戒掉的直覺。

你回想一下 Day 4 那條時間軸,你真正在讀的是哪幾條列?CUDA HW(kernel 跟 memory)、CUDA API、還有 NVTX。而這幾條列,剛好就是 cudanvtx 這兩個 trace 來源畫出來的。

  • cuda 負責錄下每一顆 kernel 的起訖、每一次 memcpy、每一個 CUDA API 呼叫。這就是你判斷三種死法的全部依據。
  • nvtx 負責錄下你程式裡的標記(forward、backward、哪個 iteration),讓那些 kernel 對得回你的程式碼。

順便講個冷知識,nvtx 幾乎是免費的。NVTX 的標記函式在沒有 profiler 掛著的時候,就是一個什麼都不做的空殼呼叫;要等 nsys 掛上來,它才真的開始記錄。所以程式裡的 NVTX 標記平常留著不拔也沒差,這也是 cuda,nvtx 敢當預設起手式的原因之一。

換句話說,cuda,nvtx 不是"最陽春的設定",是"剛好夠你回答『GPU 在做什麼、有沒有在等』的設定"。

那什麼時候才加別的?舉幾個實際情況你就有感。多卡訓練想看八張卡之間的通訊,加 nccl(Day 18 會用到);懷疑是 dataloader、檔案 I/O、系統呼叫在拖後腿,加 osrt 去看 CPU 側到底卡在哪個 system call;想知道某個 cuDNN 或 cuBLAS 呼叫內部展開成哪幾顆 kernel,才加 cudnncublas。原則永遠一樣,你是為了回答一個具體問題才加一個 domain,不是先全開再說。每加一個 domain,都是拿 overhead 去換資訊,天下沒有白吃的午餐。

為什麼要"最少",不是"越多越好"

這裡有個很多人沒意識到的陷阱,profiling 本身會影響它要量的東西。這在科學上叫觀測者效應,在這裡很具體,你每多追蹤一個東西,nsys 就得在每個事件發生時多寫一筆記錄,這件事本身要花時間、也吃記憶體。

講具體一點它是怎麼記的。nsys 會把一層攔截庫掛進你的 process,每個 CUDA API 呼叫經過它,就被蓋一個 CPU 側的時間戳再放行;kernel 在 GPU 上實際的起訖,則是 GPU 那頭記的時間戳,錄完再對回來。所以 overhead 主要落在 CPU 側,而且跟事件數成正比,你的程式一秒發射幾萬次 launch,就多幾萬次攔截。

所以會發生兩件壞事。

第一,你的程式被拖慢。追蹤 cuda,nvtx 的 overhead 通常在可接受範圍(多半是個位數到十幾個百分比),但如果你把 --trace 全開、再加上 --gpu-metrics-devices=all(那個會去 sample 硬體計數器、拿 SM 活躍度那些數字),overhead 會明顯往上跳。而且失真吃你多重,跟 kernel 的粒度有關,每筆攔截的成本是固定的零頭,kernel 一顆跑幾 ms,這零頭連 1% 都不到;kernel 全是幾 µs 的小不點、靠海量 launch 疊出來的,同一份零頭就變成可觀的比例。越是 launch 密集的程式,profiler 對它的干擾越重,讀數的時候心裡要先打這個折。問題是,被拖慢的那份 profile,量到的比例已經不是你原本程式的比例了,你等於拿一把會影響溫度的溫度計去量體溫。

而且這會害你更慘的一件事,去修一個根本不存在的問題。假設 profiler 把 CPU 側拖慢了,害 GPU 每一步都多等幾拍,你在 timeline 上看到一堆 idle,興沖沖跑去優化 dataloader,結果那些洞在沒開 profiler 的正常運行下根本沒那麼大。你花了三天優化一個是"被你自己的溫度計燒出來"的假問題。最小抓取就是在把這種干擾壓到最低,讓你量到的盡量接近真相。

第二,檔案爆炸。每一筆事件都要存,錄的東西越多、錄的時間越長,.nsys-rep 就越肥。一份追蹤 cuda,nvtx、只錄幾個 iteration 的檔,通常是幾 MB 到幾十 MB;但要是你 --trace 全開、又錄了整場好幾個小時的訓練,它可以長到好幾 GB,肥到 GUI 根本打不開,你連看都沒得看。

最小抓取要解決的,就是這兩件事。錄剛好夠用的東西、錄剛好夠用的時間,你才會得到一份"量得準、又打得開"的檔。

舉個栗子,包一支真的 PyTorch 訓練

假設你有支 train.py,平常這樣跑:

python train.py --batch-size 32 --steps 1000

要錄它,你不動任何參數,只在前面掛上 nsys profile

nsys profile --trace=cuda,nvtx -o train_min \
  --force-overwrite=true \
  python train.py --batch-size 32 --steps 1000

--force-overwrite=true(可以縮寫成 -f true)是讓它覆蓋同名舊檔,不然你重跑會被擋。跑完你會拿到 train_min.nsys-rep,接著就回到你已經會的兩招,nsys-ui train_min.nsys-rep 用眼睛看,或 nsys stats train_min.nsys-rep 算一張總帳,想偷懶還能丟給 nsys analyze 讓它自己掃一輪毛病。前兩天練的功夫,從今天起全用在你自己錄的檔上。

順帶戳破一個心理障礙,nsys profile 產出的就是一個檔案而已。所以你完全可以在遠端那台有卡的機器上錄,把那個幾十 MB 的 .nsys-repscp 拉回自己的筆電,再用 nsys-ui 慢慢開。錄跟看可以是兩台不同的機器,這也是為什麼前幾天我一直說,沒有 GPU 也能學會讀圖,你缺的從來不是卡,是一份錄好的檔。

你會發現一個問題,--steps 1000 會錄很久、檔也很肥。這裡先偷偷加一個煞車,錄個幾十步就夠你看清楚一個 iteration 長怎樣了,真的要精準只圈某幾個 iteration,是明天 Day 7 的主題。今天你只要先體會到"一行就能錄、錄出來就能用"這件事。

兩個一定會踩的坑

第一個坑,錄出來的檔大得離譜。你興沖沖加了 nsys profile 就去跑整場訓練,回來一看 .nsys-rep 三十幾 GB,nsys-ui 一打開就轉圈圈轉到地老天荒。這不是你設錯,是你沒踩煞車。解法明天講,但今天先養成一個習慣,錄之前先問自己一句,我真的需要錄這麼久嗎?多數時候,幾十步就夠。

第二個坑更陰險,明明掛了 nsys profile,打開卻發現 CUDA HW 那條列空空如也,一顆 kernel 都沒有。十之八九,是因為真正在幹活的不是你下指令的那個主行程。像 PyTorch 的 DDP、torchrun 起的多卡訓練、或很多推論框架,實際跑 CUDA 的是它 fork 出來的子行程,而你的 nsys 只盯著那個沒做事的父行程,當然錄到一片空白。多進程、多卡的錄法有它自己的眉角(會用到像 --trace-fork-before-exec=true 這類旗標),我們留到 Day 9 進容器、Day 18 上多卡再專門處理。今天你的栗子,先確保是一支單進程能直接跑起來的程式,別一開始就挑戰地獄難度。

所以錄完,養成一個三秒鐘的檢查,打開檔案先看 CUDA HW 那條列上有沒有一堆 kernel。懶得開 GUI 的話,nsys stats --report cuda_gpu_kern_sum 對它跑一下也行,表上有東西就是有錄到,吐一張空表給你就是白錄一場。有 kernel,才代表你錄到了正確的東西;空的,別急著分析,先回頭確認是不是踩到上面第二個坑。這一步能幫你省下"分析了半天才發現根本沒錄到"的冤枉時間。

幾個你遲早會加上去的旗標

cuda,nvtx 是開戰的最小組合,但有幾個旗標你很快會用到,先讓你有印象:

  • --sample=none:關掉 CPU 週期性取樣(預設是對整個 process tree 取樣)。取樣本身也有 overhead,在容器或你只關心 GPU 的時候可以關掉,這個 Day 9 進容器會再遇到。
  • --stats=true:錄完當場把 Day 5 那組報表(cuda_gpu_kern_sum 那些)直接印在終端機,錄影跟算帳一條龍,適合在遠端機器上立刻瞄一眼 top kernels。它是錄完才離線算的,不會增加錄影當下的 overhead,加了不虧。
  • --gpu-metrics-devices=all:把 SM 活躍度那類硬體計數器也錄進來(就是 Day 3 說的、比 util 誠實的數字)。它很有用,但有額外 overhead,不是每次都要開。
  • --cuda-memory-usage=true:連 GPU 記憶體的配置與用量曲線一起錄,查 OOM、抓記憶體一路長高的洩漏時很好用,一樣是拿 overhead 換的。
  • -c cudaProfilerApi:只在程式呼叫特定 API 的那段才開錄,這是明天 Day 7 精準抓取的鑰匙。

現在你不用背這些,只要知道,nsys profile 是一個"先用最小組合開戰、需要什麼再逐個加"的工具,不是一次要你把所有旗標都搞懂的怪獸。

最小抓取 vs 全開全錄:左邊 cuda,nvtx 只錄幾個 iteration,小檔、低 overhead、量得準;右邊 trace 全開又錄整場,幾 GB、高 overhead、量到失真

小結與明天

今天你從"看別人的錄影"畢業,學會自己按錄影鍵。要記住的其實就一行 nsys profile --trace=cuda,nvtx -o out python …,加上一個心法,錄最少能回答問題的東西。這行值得你現在就抄進筆記,因為從今天起,這一季每一份要分析的檔,都會是你自己用它錄出來的。全開全錄不是專業,是新手才會犯的貪心,它只會給你一個又慢又肥、還量不準的怪物。反過來,會不會用最小組合、懂不懂 overhead 這個取捨,其實就是分辨一個人有沒有真的做過 profiling 的分水嶺。

但你也發現了,就算只錄 cuda,nvtx,錄一支跑很久的訓練還是會又臭又長。明天 Day 7,我們就來解決這個,怎麼像外科手術一樣,只切下你真正想看的那兩三個 iteration,讓一份原本幾 GB 的檔,瘦成幾 MB、還更好讀。

攝影機你會按了。明天,我們學會只錄關鍵那幾秒。

參考資料


上一篇
Day 5|一萬顆 kernel 用眼睛看不完,讓 nsys 自己算給你看
下一篇
Day 7|profile 肥到打不開?圈三個 iteration 就夠
系列文
GPU很忙?他真的有在做事嗎?7
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言