港口的規矩跟馬路上不一樣。你的車開進貨櫃,行車記錄器隔著鐵皮什麼都拍不到;海關不點頭,設備連開機都不給你開。在這裡,錄不到影從來不是技術問題,是通關問題。
說白了,今天一行新指令都不用學,要學的是開門。
前三天你練起來的那套,錄、圈、標,全都假設你直接在一台機器上跑 python train.py。真實世界的 job 十之八九住在 Docker 容器裡、排在 Slurm 隊伍裡。同一行 nsys profile,搬進去就開始出怪事,取樣起不來、CUDA HW 列一片空白、報表被拒收。九成的情況是三件事沒喬好,工具不在、權限不夠、行程不對,而權限那關其實藏著兩道不同的門,加起來正好四道。一道一道過。
最基本的一件事,nsys 這支程式得在容器裡面。你在 host 裝得再齊,容器裡沒有就是沒有。
好消息是,如果你用 NVIDIA 的 NGC 映像檔(nvcr.io/nvidia/pytorch:xx.xx-py3 這系列),nsys 出廠就在裡面,nsys --version 就能確認。自己 build 的映像檔就得自己裝,或把 host 的安裝目錄掛進去。
這裡藏著一個版本眉角。.nsys-rep 的相容性是單向的,新版 GUI 開得了舊版 CLI 錄的檔,反過來不行。所以規矩是,看檔那台(你的筆電)的 Nsight 版本,要大於等於錄檔那台(容器裡)的版本。在容器裡 nsys --version 一下,回你筆電對一眼,十秒省掉一次"檔案打不開"的驚嚇。
工具上船之前,卡也要先看得到。容器要摸到 GPU,靠的是 nvidia-container-toolkit 把 host 的 driver 掛進來,Docker 這邊的開關就是 --gpus all。進去先跑一次 nvidia-smi,有卡再談錄影。另一個新手常撞的細節,容器裡的 GPU 編號是重新編過的,你要了兩張卡,進來就叫 0 跟 1,跟 host 上的 3 跟 7 對不起來。要跨容器對卡,看 nvidia-smi -L 列的 UUID 才算數,編號只在自己船艙裡有效。
上船之後,先別急著錄。nsys 內建一個安檢櫃檯:
nsys status --environment
它會把這台環境的體質全列出來,一項一項給你 OK 或 Fail,長相大概是這樣(示意):
Timestamp counter supported: Yes
Sampling trigger event supported: Yes
Linux Kernel Paranoid Level = 2: OK
Linux Distribution: Ubuntu
Linux Kernel Version: 5.15: OK
CPU Profiling Environment: OK
perf 的 paranoid level 是多少、能不能取樣,全在上面。在容器裡這行尤其值得先跑,因為它能把你即將撞上的門提前指出來,不用等錄完才發現白錄。先安檢,再錄影,這習慣在容器裡值一百分。
第一個常見的 Fail 長這樣,nsys 一開場就抱怨 CPU sampling 起不來。
機制要先懂一件事,容器不是虛擬機,它跟 host 共用同一顆 kernel。CPU 取樣用的 perf_event_open 是 kernel 的功能,開不開放看的是 host 的 kernel.perf_event_paranoid 設定,文件要求 2 以下才給採。你在容器裡改不動它,因為 /proc/sys 是 host 的地盤,這也是為什麼這道門常常要找管機器的人開。
兩條路。正路是開權限,Docker 跑的時候補一句:
docker run --gpus all --cap-add=SYS_ADMIN ...
--cap-add=SYS_ADMIN 給容器 perf_event_open 的門票,官方文件掛保證的就是這個 capability。它是一把大鎖匙,安全上先跟你的 infra 團隊打聲招呼。
偷吃步是繞過去,--sample=none。Day 6 就預告過這招,把 CPU 週期性取樣關掉,這道門根本不用過。判準很實際,你今天的問題需不需要 CPU 側的取樣資料?我們這一季的主線是 GPU 的 timeline,cuda,nvtx 的追蹤走的是另一條路、不吃 perf 權限,所以多數日子你可以大方關掉取樣,不用為了一個用不到的功能去跟人要 root。
別把兩種權限搞混了,這是容器裡最容易鬼打牆的地方。
上一道門管的是 CPU 取樣。但如果你開了 --gpu-metrics-devices=all(Day 6 提過的 SM 活躍度那些硬體計數器),你面對的是另一道完全不同的門,NVIDIA driver 對 GPU performance counter 的管制。被擋的時候會看到一個經典的錯誤代號 ERR_NVGPUCTRPERM,意思是這台機器的 driver 預設只讓 admin 讀計數器。
開這道門要動到 host 端的 driver 參數,NVreg_RestrictProfilingToAdminUsers=0,這是改 kernel module 設定,重載 driver 才生效,雲端環境跟共用叢集上你多半沒這個權力,某些多租戶平台甚至根本不開放。
所以判準跟上一道門一樣,先問要不要。最小組合 cuda,nvtx 完全不碰硬體計數器,一般使用者權限就錄得動。Day 6 說最小抓取是拿 overhead 換資訊,今天再加一句,最小抓取連要的權限都最小。真需要計數器那天(Day 21 之後下細查層會遇到),再去敲 infra 的門。
這是前三天一路埋的伏筆,今天總收帳。
Day 6 說過那個經典慘案,錄完打開,CUDA HW 一片空白,因為真正幹活的不是你下指令的行程。Day 8 又補了一刀,DataLoader 的 worker 是 fork 出去的另一個 process。現在把機制講完整。
Linux 的 Python multiprocessing 預設用 fork 生小孩,而 fork 出來的小孩不會接著呼叫 exec 把自己換成新程式,它就以複製體的身分一路做事。nsys 預設只在小孩 exec 的時候接手追蹤,對這種 fork 完不 exec 的小孩,等於沒人跟車。你的 kernel 都是小孩發的,主行程乾乾淨淨,錄出來自然一片白。
解法一行:
nsys profile --trace=cuda,nvtx --trace-fork-before-exec=true -o out python train.py
但要附上官方的原話等級警告,追蹤 fork 到 exec 之間的行為踩在未定義行為上,極端情況可能讓程式 crash 或 deadlock,所以它才不是預設值。開了它就多留意 job 有沒有異常。
不想吃這個風險,還有第二條路,從 Python 那頭改。把 multiprocessing 的生小孩方式從 fork 換成 spawn,spawn 是重新啟動一個乾淨的直譯器,走的是 fork 完馬上 exec 的路,nsys 預設就會跟。DataLoader 給你開關,DataLoader(..., multiprocessing_context="spawn") 一個參數的事。代價是 spawn 的 worker 啟動比 fork 慢,而且不能繼承父行程的全域狀態,有些 code 要小改。兩條路挑一條,別兩個坑一起踩。
順帶把 torchrun 講清楚。torchrun 起 rank 走的是 fork 完接著 exec 的路,屬於 nsys 預設就會跟的類型,所以單機多卡把整包 launcher 錄起來通常行得通,只是所有 rank 疊在同一份檔裡。至於一 rank 一份怎麼錄得漂亮,馬上講。
兩個跟檔案系統有關的隱形地雷。
第一個,容器的檔案系統是用完即丟的。你把 -o 寫在容器裡的路徑,容器一刪,報表陪葬。規矩是 -o 永遠指向掛載進來的 volume(-v $PWD:/work 那種),錄完檔案就在 host 上,接著照 Day 6 的老套路 scp 回筆電慢慢看。
第二個,nsys 錄影過程的中繼資料會走系統暫存目錄,而容器的 /tmp 常常是個很小的 tmpfs。錄長一點就爆給你看,錯誤訊息還常常長得不像空間問題。保險做法是把 TMPDIR 指到掛載的大空間去,跟 -o 一樣的道理,別把大東西寫在船艙裡。
叢集上的原則一句話,nsys 要跟著 job 一起進節點,不是在 login node 上錄。login node 沒有卡,你在那裡錄到的只有寂寞,而且還會被管理員追殺。
先補一個環境現實,學術叢集跟不少企業 HPC 上跑的常常不是 Docker,是 Apptainer(舊名 Singularity)。別緊張,道理全部通用,nsys 一樣要在映像檔裡、報表一樣要寫到綁定進來的目錄。它的權限模型跟 Docker 不一樣,能不能拿到 perf 這類權限,多半在管理員裝機的時候就定好了,不是你 job 裡一個旗標能翻盤的。但 perf 那道門照樣看 host kernel 的 paranoid level,因為容器共用 host kernel 這件事,在哪套容器技術上都成立。過不了,一樣是 --sample=none 先走,或找管理員。
做法是把 nsys 塞進 srun 跟你的程式中間:
srun nsys profile --trace=cuda,nvtx \
-o report_%q{SLURM_PROCID} python train.py
亮點在 %q{SLURM_PROCID},nsys 的輸出檔名支援環境變數展開,Slurm 給每個 rank 的編號直接變成檔名,八個 rank 就是 report_0 到 report_7,一人一份、不打架。這招不限 Slurm,%q{} 裡塞任何環境變數都行,MPI 的 rank 變數同理。
不過八張卡各錄一份,常常是浪費。DDP 的 rank 之間本來就長得像(這正是 Day 7 那套 steady state 邏輯),實務上很常見的省法是只錄 rank 0,其他 rank 素顏跑:
srun bash -c 'if [ "$SLURM_PROCID" -eq 0 ]; then
nsys profile --trace=cuda,nvtx -o report_r0 python train.py
else python train.py; fi'
一份檔換八分之一的 overhead 跟儲存。當然,要抓的是"某張卡特別慢"這種 rank 之間不對稱的病,就得全錄,那是 Day 18 的主題,多卡的檔怎麼合著看也留到那天。
Docker 的部分,把今天的門票全數上齊,就是這一行:
docker run --gpus all --cap-add=SYS_ADMIN \
-v $PWD:/work -w /work \
nvcr.io/nvidia/pytorch:25.06-py3 \
nsys profile --trace=cuda,nvtx -o /work/report python train.py
拆開對照今天的四道門,--gpus all 讓你看得到卡、NGC 映像檔讓 nsys 在船上、--cap-add=SYS_ADMIN 開 perf 的門(不需要就拿掉、加 --sample=none)、-v 加 -o /work/... 讓報表落在船外。有些環境的 seccomp 政策管得更緊,cap 加了還是被擋,那就不是你的錯了,拿著錯誤訊息去找 infra,比自己瞎試快十倍。
K8s 上同一道門換個門把,Pod spec 裡 securityContext.capabilities.add: ["SYS_ADMIN"],其餘邏輯一模一樣,volume 換成 PVC、映像檔照舊。
再往上一層,還有 Modal 這種 serverless GPU 平台,我們自己就有 job 跑在上面(對,就是 Day 3 借三層 util 概念的那家)。這種平台連 docker run 那行都輪不到你打,容器是平台包好直接發車的,--cap-add 沒地方加、host 更不是你的,perf 跟 GPU 計數器的門基本上就是關著的,你想開也沒有門把。但也正因為這樣,今天的判準在上面全數適用,cuda,nvtx 不吃特權照錄、--sample=none 把取樣大方關掉、nsys 裝進 image、錄完把 .nsys-rep 寫進平台的 Volume 再拉回本機開。門不在你手上的地方,最小抓取不是選項之一,是唯一那條路,而它剛好夠用。
這不是我單方面的說法,Modal 自己的文件就把話講明白,儀表板上的 GPU metrics 只是訊號、不能拿來 debug 效能,要靠 tracing 跟 profiling,跟 Day 3 我們借他們三層 util 時的結論是同一句。他們官方給的 profiling 範例走的是 torch.profiler 那條路(Day 8 說過的另一支耳朵),要走 nsys 這條,就照今天的套路自己把工具帶上船。
把今天的門全部釘在一張單子上,錄之前掃一眼:

搭配 Day 6 那個三秒檢查收尾,錄完 ls -lh 看量級、nsys stats --report cuda_gpu_kern_sum 看表上有沒有 kernel。空表在容器裡九成就是第四道門,回去補 --trace-fork-before-exec=true。
今天沒有新的錄影技巧,只有四道門。工具要上船(NGC 自帶,版本看檔那端要新)、CPU 取樣走 kernel 的 perf 門、GPU 計數器走 driver 的門(而 cuda,nvtx 這兩道權限的門都不用過)、fork 的小孩要人跟(--trace-fork-before-exec 或改 spawn)。門之外還有兩顆地雷,檔案要落在船外(volume 跟 TMPDIR),叢集要進節點錄、檔名用 %q{} 分家。再加一個新習慣,進容器先 nsys status --environment 安檢一輪。
到今天為止,錄影的功夫算是齊了,你能在自己機器、容器、叢集上,錄出一份圈得準、標得清楚的 .nsys-rep。接下來的問題變成,檔案一打開,該按什麼順序看,才能三分鐘內講出這份 job 健不健康。明天 Day 10,我們把前面學的整理成一套固定的體檢流程。
門都開了。明天,開始看診。
--cap-add=SYS_ADMIN 與 perf_event_paranoid 要求、nsys status --environment:Nsight Systems User Guide、Installation Guide。ERR_NVGPUCTRPERM、NVreg_RestrictProfilingToAdminUsers):NVIDIA — Permission issue with Performance Counters。--trace-fork-before-exec 與輸出檔名 %q{ENV_VAR} 展開:Nsight Systems User Guide — Profiling from the CLI。