iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
AI Engineering

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

Day 4|別急著開Agent叫他幫你看,先學會看懂一條時間軸

  • 分享至 

  • xImage
  •  

前三天,我們一直在跟那個跳錶過不去
util 100%、SM Active 只有 15%、三種死法,全都是在講同一件事,那支錶不可信。可是光罵錶沒有用,你總得有個能信的東西來代替它。今天就換掉它。

每台小黃都裝行車記錄器。時速表只告訴你"現在"跳到多少,行車記錄器卻能讓你回放剛剛那個路口,到底是等紅燈、還是被人插隊、還是根本開錯路。要查為什麼這趟沒賺到里程,你要調的是記錄器,不是盯著時速表發呆。

Nsight Systems 就是 GPU 的行車記錄器。它會把 GPU 剛剛那幾秒鐘的一舉一動,錄成一條時間軸。今天你不用學會怎麼錄、也不用學會怎麼分析,只要學會一件事,打開一份錄影檔,看懂上面那幾條列在說什麼。這就夠你開戰了。

為什麼記錄器可信,時速表不可信

差別是"抽樣"跟"全記錄",這也是為什麼前三天那支錶會騙你、今天這條時間軸不會。

Day 3 我們拆過,nvidia-smi 是個抽樣器(sampler)。它每隔一小段時間偷看一眼、問一句"現在有 kernel 在跑嗎",其他時間發生什麼它一概不知。它給你的是一張一張快照,快照跟快照中間全靠猜。

Nsight Systems 是個追蹤器(tracer)。它掛在 CUDA 底下,把每一顆 kernel 的起訖時間、每一次 memcpy 的大小、每一個 API 呼叫,全部逐筆記下來,時間精度到微秒等級。沒有一格是猜的,全都是量到的。正因為它記的是"事件"而不是"快照",它才能還原出精確的先後順序,也才回答得了那個時速表永遠答不出的問題,誰在等誰。抽樣器天生就做不到這件事,它手上根本沒有那些事件。

代價是,全記錄有 overhead。錄越久,檔越肥、對程式本身的干擾也越大,所以一份 profile 通常只錄短短幾秒、圈幾個 iteration 就夠了(怎麼錄得剛剛好、不錄成一個幾 GB 的怪物,期待Day 7)。記住這個取捨,它會解釋後面很多"為什麼不乾脆整場訓練都錄下來"的問題。

先別貪,工具一個就好

打開工具之前,先回到我們 Day 1 那張工具地圖。你這三天一直站在最上面的"表象層",跟 nvidia-smi 那支錶纏鬥。今天,我們往下走一格,踏進"量測層"。

工具地圖:表象層(smi,會騙人)已經走過,今天踏進量測層(Nsight Systems);解讀與細查層之後才碰

新手最容易犯的錯,就是一次把十個工具全打開。nsys、ncu、NVBit、CUTracer、一堆看起來很專業的東西,結果每個都只會按一半,最後什麼都沒看懂。我們這一季反過來做,主線工具就一個,Nsight Systems,它只負責回答一個問題,"時間軸上,誰在等誰"。其他工具(像 Nsight Compute)是等你在時間軸上找到肥點、需要放大單顆 kernel 時才下去的"細查層",今天完全不用碰。

一次學一格。地圖上這一格顧好,比同時開十個工具有用一百倍。

裝一個,打開一份

好消息是,你沒有 GPU 也能做今天的事。因為"打開一份錄影檔來看"這件事,不需要卡,就像你不用有攝影機也能播別人拍的影片。

# 1) 裝 Nsight Systems(含 GUI 與 CLI),到官網 Get Started 選你的平台
#    https://developer.nvidia.com/nsight-systems/get-started

# 2) 直接抓一份現成的 8 卡訓練 profile(.nsys-rep,約 11 MB,不用裝別的東西)
pip install -U "huggingface_hub[cli]"
hf download GindaChen/nsys-hero \
  distca-0/baseline.t128k.host-fs-mbz-gpu-899.nsys-rep \
  --repo-type dataset --local-dir .

# 3) 用 GUI 打開它
nsys-ui distca-0/baseline.t128k.host-fs-mbz-gpu-899.nsys-rep

副檔名 .nsys-rep 就是 Nsight Systems 的錄影檔格式。這份是一段真實的多卡訓練,你打開之後看到的,是八張卡當時到底在忙什麼。

先給你一點心理準備,這是一份 8 張卡的 Megatron 訓練。你會看到最上面有一棵 NVTX 的樹,一層層標著 Iteration、forward、backward 這種名字,那是這份錄影的"目錄",讓你知道現在看的是訓練的哪一段。再往下是每張卡的 CUDA HW 列,上面密密麻麻的色塊大多是矩陣乘法跟 attention 的 kernel,偶爾夾著幾條長相特別的,那是八張卡在彼此同步的 NCCL 通訊。這些名詞你現在通通不用懂,只要知道一件事,這一份就是我們接下來好幾天會反覆解剖的"同一具大體"。今天先學會把它打開、擺正,之後每天在它身上多認一個器官。

時間軸解剖,先認得四條列

打開之後畫面會有點嚇人,一條橫向時間軸,左邊排了一大排列名,密密麻麻。別慌,今天你只要認得四種列就好,其他之後用到再說。

Nsight timeline 解剖:CPU 執行緒(發指令)、CUDA API(下訂單)、CUDA HW(GPU 實際在算的 kernel 與在搬的 memory)、NVTX(程式標記)四條列,綠=在算、灰=發呆、橘=在搬

  • CPU threads:你的 Python 跟框架在 CPU 上做的事,包含它"發指令"叫 GPU 去算。
  • CUDA API:CPU 對 CUDA 下的那些呼叫(cudaLaunchKernelcudaMemcpy…)。你可以把它想成"下訂單",訂單下在 CPU 這邊。
  • CUDA HW(GPU):GPU 上"實際發生"的事,也就是"出貨"。它又分兩條,一條是 kernel(在算),一條是 memory(在搬)。這是今天的主角,因為前三天吵半天的"有沒有在做事",答案就寫在這條列上。
  • NVTX:如果程式有加標記,這裡會用一段段色塊告訴你"這段是 forward、那段是 backward"。現在這份可能有、可能沒有,我們期待Day 8 再自己動手標。

訂單(CUDA API)下在 CPU,出貨(CUDA HW)發生在 GPU。這兩條列一對照,你就會看到一件 nvidia-smi 永遠告訴不了你的事,CPU 到底有沒有跟上 GPU。

至於其他那幾十條列(OS runtime、cuDNN、cuBLAS、NCCL、記憶體用量…),今天全部先無視。它們不是不重要,是"還沒輪到"。多卡的 NCCL 我們期待Day 18 才會認真看,現在你只要記得,畫面上絕大多數的列,今天都可以先收起來。

這條時間軸真正的價值,是"誰在等誰"

一個數字沒辦法告訴你因果,一條時間軸可以。

Nsight 有個很好用的功能,你在 CUDA HW 上點一顆 kernel,它會拉一條線幫你連回 CPU 上"是誰、在什麼時候下令發射它的"。這條線是整份錄影最值錢的東西。因為當你看到 GPU 空了一段(發呆),順著線往上看 CPU 那條列,答案通常就在那,是 CPU 自己還在忙、來不及發下一道指令,GPU 只好在下面乾等。

這就是為什麼我一直說 timeline 回答的是"誰在等誰"。GPU 慢,很多時候根本不是 GPU 的錯,是它前面那個 CPU 拖了後腿。這種因果關係,你拿一百個 nvidia-smi 的數字也拼不出來,但在時間軸上,它就是一條線的事。

誰在等誰:CPU 發指令、GPU 才算;CPU 一忙不過來,GPU 就空出一段發呆

讀圖,其實只看三件事

認得列之後,讀時間軸其實只看三件事,而且這三件事你前三天早就學過名字了。

第一,kernel bar 有多密。 CUDA HW 的 kernel 那條列,一個個實心色塊就是 GPU 在算。塞得密密麻麻、一條接一條,代表它在認真做事,這就是 Day 2 的"算不動",好的那種忙。

第二,空白在哪。 kernel 跟 kernel 之間的空隙,就是 GPU 在發呆(Day 2 死法一)。這正是 Day 3 那個 util 死也不肯告訴你的真相,nvidia-smi 只會說 100%,但時間軸會直接把那些洞畫在你眼前。看到一段一段整齊的空白,你就抓到兇手了。

第三,memory 條多不多。 memory 那條列如果 memcpy 一大堆、還常常卡在 kernel 前面擋路,那就是在"搬不完"(Day 2 死法二),資料流變成了瓶頸。

這三種花紋,還能再細分出幾個一看就懂的簽名。CPU 的 CUDA API 列上 cudaLaunchKernel 一顆接一顆擠成密密一片、GPU 那條卻斷斷續續跟不上,那是 launch-bound,CPU 發射的速度追不上 GPU 執行的速度(Day 15 專門修)。或者 memory 列上一條長長的 H2D memcpy 卡在每個 iteration 的開頭、擋在第一顆 kernel 前面,那是 dataloader 餵資料餵不贏 GPU 吃的速度(Day 16)。看到這些形狀,你連 profile 都不用往下讀太多,病名已經寫在臉上了。這就是為什麼老手掃一眼 timeline 就能講出個大概,他們認的不是每一顆 kernel,是這幾種形狀。

這裡有個新手最常犯的錯,看錯列。很多人一看到 CPU 那條列排得滿滿的,就以為 GPU 很忙。不對,CPU 忙不等於 GPU 忙,甚至常常剛好相反,CPU 忙著追進度、GPU 在下面納涼。要判斷 GPU 到底有沒有在做事,你的眼睛只能盯 CUDA HW 那條列,其他列都是背景配角。這條規矩今天先立起來,之後省下你大把冤枉時間。

換句話說,前三天我們用講的、用比喻的那三種死法,今天第一次"有臉"了。你不用再靠想像,它們在時間軸上就是三種一眼能分辨的花紋。

第一次打開,先做三個動作

第一次打開這種圖,九成的人會被那滿滿一整屏的線嚇到,然後默默關掉。先別。做三個動作,它就會乖下來。

一,放大。整份 profile 可能有好幾秒、上萬顆 kernel,全塞在一屏你什麼都看不清。用滾輪放大到畫面上只剩"一兩個 iteration"的範圍,一下就乾淨了。

二,找一個 iteration。訓練是一圈一圈重複的,你會看到時間軸上有一段花紋不斷重複出現,那一段就是一個 step。看懂一個 step,就等於看懂整份錄影,因為後面每一圈長得都差不多。

三,收合用不到的列。把今天不看的那幾十條列收起來,只留 CUDA HW(kernel 跟 memory)跟上面的 CPU。畫面剩三四條,故事馬上浮出來。

這三個動作,我自己第一次打開 Nsight 的時候沒人教,對著滿屏的線發呆了大半個小時。你不用重蹈覆轍,記得先放大、找一圈、收乾淨。

錶說滿格,時間軸說漏成篩子

Day 3 那個 util 100% 卻只有 15 個 SM 在動的鬼故事,在時間軸上就是 CUDA HW 那條列有 kernel 在跑(所以 util 給你 100%),但每顆都又小又稀、中間全是洞。錶看的是"有沒有在跑",時間軸看的是"跑了多少、漏了多少"。同一張卡、同一段時間,一個騙你、一個救你,這就是我們寧可多花五分鐘打開錄影的原因。

這份錄影,不只能用眼睛看

.nsys-rep 是 Nsight 自己的二進位錄影格式,給 GUI 開來看很方便,可是你沒辦法拿它去寫個腳本、自己撈數字。所以 Nsight 附了一支 nsys export,能把同一份錄影轉成別種「機器讀得動」的格式,指令長這樣:

nsys export --type sqlite -o out.sqlite my_profile.nsys-rep

--type 後面能填的,照官方最新文件(2026.1)是這幾種:

  • sqlite:預設值,也是最常用的。整份錄影會變成一顆 SQLite 資料庫,每顆 kernel、每次 memcpy 都是一列,你可以直接下 SQL 去問「最肥的十顆 kernel 是誰」。明天 Day 5 那支 nsys-ai,骨子裡吃的就是這個。
  • arrowparquetdirarrowdir:給資料分析用的欄式格式,適合搬進 pandas、DuckDB 這類工具做大量統計。
  • hdf:科學計算界常見的格式,只在 x86_64 的 Linux 跟 Windows 撐得住。
  • jsonlines:一行一筆事件的 JSON,人跟程式都讀得懂,適合快速 grep 或丟給別的程式接。
  • info:不真的轉檔,只印出這份錄影裡有哪些資料表、各有幾列,拿來探路很好用。

不用背。你現在只要記一件事,.nsys-rep 不是死檔,它能攤成 sqlite 讓程式去撈。這正是明天的伏筆,眼睛看不完上萬顆 kernel,那就把它轉成一張表,讓工具替你算。

小結與明天

今天你只做了一件很小的事,打開一份 .nsys-rep 錄影、認得四條列、學會讀三件事(kernel、空白、memory)。聽起來不多,但這是整季所有診斷的地基,後面每一天,我們都在這條時間軸上工作。

不過用眼睛一格一格看,很快就會遇到瓶頸,一份幾秒鐘的訓練 profile 動輒上萬顆 kernel,你不可能一個個數。明天 Day 5,我們換一個工具打開"同一份"檔案,讓它不只是攤開給你看,還能直接讀給你聽,把"這份 profile 有沒有生病"用一句話講出來。

行車記錄器裝好了,也會看了。明天,我們讓它自己開口報告。

參考資料


上一篇
Day 3|nvidia-smi 100%?恭喜,你可能還是不知道詳細情況
系列文
GPU很忙?他真的有在做事嗎?4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言