核心概念
torch.profiler 是 PyTorch 內建的效能分析工具,輸出兩種互補的分析產物:
- Profiler Table:統計摘要,回答「哪個操作最耗時」
- Profiler Trace:時間軸視圖,回答「操作在什麼時間點發生、為什麼發生」
兩者缺一不可。Table 找熱點,Trace 找根因。
Overhead-Bound vs Compute-Bound
這是理解 GPU 效能的根本對立:
| 狀態 | 現象 | 問題 |
|---|---|---|
| Overhead-bound | CPU dispatch 時間 >> GPU 計算時間 | GPU 大部分時間閒置 |
| Compute-bound | GPU 計算是瓶頸 | 理想狀態——GPU 持續工作 |
實測:64×64 矩陣 → CPU time 2.3ms,GPU time 23μs(GPU 閒置 98%)。改成 4096×4096 → CPU 4.9ms / GPU 4.5ms,進入 compute-bound。
基礎設定
with torch.profiler.profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
schedule=torch.profiler.schedule(wait=1, warmup=1, active=3, repeat=1),
) as prof:
for _ in range(5):
step(); prof.step()
prof.export_chrome_trace("trace.json")
wait:跳過不穩定的初始步驟warmup:不記錄的預熱(讓 GPU clock 進入穩態)active:實際錄製的步驟數
關鍵要點
冷啟動延遲不可忽略:第一個 profiling step 比後續慢 ~228μs,含 workspace 分配、cuBLAS 啟發式選擇、延遲模組載入。正式 profile 前必須加暖機迭代。
CPU-GPU Lane 時間差是正常的:Trace 中 CPU 和 GPU 道通常有 2-5ms 偏移,代表 kernel 提交到實際執行的延遲,不是 bug。
cudaOccupancyMaxActiveBlocksPerMultiprocessor 的語意:此查詢只出現在 GEMM、卷積等重量級 kernel 前,elementwise 操作不會觸發。cuBLAS 有數百種 kernel 變體,需要查詢硬體容量才能選出最優配置。
Kernel 時間的自然波動:相同操作在不同迭代時間差異大,原因是 GPU clock 受熱量與電源管理影響。Table 平均值會遮蓋這種現象,Trace 才看得出來。
torch.compile 的融合發生在 dispatcher 層,不在 kernel 層:Eager mode aten::add + aten::mm → Compiled mode aten::addmm 單一 dispatch。GPU 仍然跑同一個 cuBLAS GEMM kernel,差別是 bias addition 被折入 epilogue,同時觸發一次 Device-to-Device memcpy 複製 bias。
實務應用
快速診斷清單
| 現象 | 含義 | 行動 |
|---|---|---|
| CPU time (ms) >> GPU time (μs) | Overhead-bound | 增加 batch size 或融合操作 |
| 第一個 ProfileStep 明顯更寬 | 冷啟動開銷 | 加 warmup 迭代 |
GEMM 前有 cudaOccupancyMaxActiveBlocksPerMultiprocessor |
正在選擇 cuBLAS kernel 變體 | 正常,表示重量級 kernel |
長 cudaDeviceSynchronize |
Profiler 刷新 buffer;GPU 還在工作 | 勿誤判為 idle |
意外的 cudaMemcpyAsync |
bias 的 DtoD copy(addmm epilogue) | torch.compile 預期行為 |
視覺化:將 .json 上傳至 Perfetto UI,鍵盤 WASD 移動,點擊 GPU kernel 可看暫存器用量與 block size。
與 Hugging Face Accelerate 整合時,可改用 ProfileKwargs 傳入,讓多 GPU 訓練迴圈自動管理 profiler 生命週期。
延伸觀點
Profiler 本身有效能代價:PerfTracker(arXiv 2025)指出 torch.profiler 每秒可產生 100+ MB trace 資料,大規模訓練下會拖慢 CUDA kernel 執行速度,且停止 profiling 後殘留影響仍存在。在生產環境做線上 profiling 時,需考慮取樣策略而非全程錄製。
記憶體 profiling 是獨立維度:Hugging Face Accelerate 文件指出,profile_memory=True 才會追蹤每個算子的記憶體分配量。純時間分析無法看到記憶體瓶頸(如大量 aten::empty 分配);複雜模型需同時看時間與記憶體兩份 Table。
Chrome Trace ≠ 所有分析的終點:GPU MODE Lecture 1 強調,torch.profiler 的 trace 只能看到 kernel launch 與 memory transfer,無法直接告訴你如何優化 kernel 本身。要從「看到瓶頸」到「知道怎麼修」,需要進一步用 Nsight Compute 分析 memory coalescing、occupancy 等硬體層指標。torch.profiler 是宏觀定位工具,Nsight Compute 是微觀調優工具,兩者互補。