com um clique
pytorch-profiler
PyTorch Profiler 性能分析,提供算子级瓶颈定位
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
PyTorch Profiler 性能分析,提供算子级瓶颈定位
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
DeepSpeed ZeRO 显存优化与训练加速
Flash Attention 版本选型与最佳配置
提交代码到远程仓库
NVIDIA Nsight Systems 系统级 GPU Profiling
Nsight Systems 自动检测、安装与升级
外网代理配置,支持受限网络环境中访问 GitHub/PyPI 等外网资源
| name | pytorch-profiler |
| description | PyTorch Profiler 性能分析,提供算子级瓶颈定位 |
使用 PyTorch Profiler 对训练或推理代码进行性能分析,定位算子耗时、显存瓶颈、GPU 利用率问题,并给出优化建议。
当用户需要对 PyTorch 训练/推理代码进行性能 profiling、定位瓶颈、分析 GPU 利用率、排查显存问题时触发。或当用户要求查看某段代码的算子耗时、CUDA kernel 时间分布等。
你是 PyTorch 性能分析专家。被调用时,根据用户场景选择合适的 profiling 方式,插入 profiler 代码,运行分析,并解读结果给出优化建议。
根据用户需求判断属于哪种场景:
| 场景 | 特征 | 推荐方式 |
|---|---|---|
| 快速算子耗时分析 | 想知道哪些算子最耗时 | 基础 profile context manager |
| GPU kernel 分析 | 想看 CUDA kernel 粒度耗时 | 启用 ProfilerActivity.CUDA |
| 显存分析 | OOM 或想了解显存分布 | 启用 profile_memory=True |
| 长训练任务采样 | 训练循环中周期性采集 | 使用 schedule + on_trace_ready |
| 可视化 Trace | 想在 Chrome/Perfetto 中查看时间线 | export_chrome_trace |
| 定位代码位置 | 想知道耗时算子来自哪行代码 | 启用 with_stack=True |
根据场景提供对应代码模板:
import torch
from torch.profiler import profile, ProfilerActivity, record_function
# 包裹要分析的代码段
with profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
record_shapes=True,
with_flops=True,
) as prof:
with record_function("training_step"): # 用户自定义标签
# ... 被分析的代码 ...
output = model(inputs)
loss = criterion(output, targets)
loss.backward()
# 打印按 CUDA 总时间排序的 Top 20 算子
print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=20))
# 也可以按 CPU 时间排序
# print(prof.key_averages().table(sort_by="cpu_time_total", row_limit=20))
# 按算子输入 shape 分组(需 record_shapes=True)
# print(prof.key_averages(group_by_input_shape=True).table(sort_by="cuda_time_total", row_limit=20))
关键参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
activities | list | 采集范围:ProfilerActivity.CPU(CPU 算子)、ProfilerActivity.CUDA(GPU kernel)、ProfilerActivity.XPU |
record_shapes | bool | 记录算子输入 tensor 的 shape,帮助定位不同 shape 的性能差异 |
with_flops | bool | 估算矩阵乘法和 2D 卷积的 FLOPS |
with_stack | bool | 记录调用栈(文件名+行号),开销较大,按需启用 |
with_modules | bool | 记录模块层级(如 ModelA.LayerB),目前仅支持 TorchScript |
profile_memory | bool | 跟踪 tensor 显存分配/释放 |
输出表格关键列解读:
| 列名 | 含义 |
|---|---|
Self CPU | 算子本身 CPU 耗时(不含子算子) |
CPU total | 算子 CPU 总耗时(含子算子) |
Self CUDA | 算子本身 GPU kernel 耗时 |
CUDA total | 算子 GPU 总耗时 |
CPU Mem / Self CPU Mem | 内存分配量(需 profile_memory=True) |
# of Calls | 调用次数 |
with profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
profile_memory=True,
record_shapes=True,
) as prof:
output = model(inputs)
loss = criterion(output, targets)
loss.backward()
# 按显存分配量排序
print(prof.key_averages().table(sort_by="self_cpu_memory_usage", row_limit=20))
# 或按总显存(含子算子)排序
# print(prof.key_averages().table(sort_by="cpu_memory_usage", row_limit=20))
# 导出显存时间线(HTML 格式可直接在浏览器查看)
# prof.export_memory_timeline("memory_timeline.html", device="cuda:0")
注意:export_memory_timeline 已标记为 deprecated,新版推荐使用:
torch.cuda.memory._record_memory_history()
# ... 运行代码 ...
torch.cuda.memory._export_memory_snapshot("memory_snapshot.pickle")
from torch.profiler import profile, schedule, tensorboard_trace_handler
with profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
schedule=schedule(
skip_first=10, # 跳过前 10 步(避免初始化干扰)
wait=5, # 每个周期等待 5 步(profiler 不活跃)
warmup=1, # 预热 1 步(结果丢弃,消除 profiling 开销偏差)
active=3, # 实际采集 3 步
repeat=2, # 重复 2 个周期后停止(0 = 无限)
),
on_trace_ready=tensorboard_trace_handler("./profiler_logs"),
record_shapes=True,
profile_memory=True,
with_stack=True,
) as prof:
for step, batch in enumerate(train_loader):
if step >= 10 + (5 + 1 + 3) * 2: # 超过采集范围可提前退出
break
train_step(batch)
prof.step() # 必须:通知 profiler 当前 step 结束
schedule 参数详解:
| 参数 | 说明 |
|---|---|
skip_first | 跳过最初 N 步(默认 0),用于跳过数据加载/模型初始化 |
wait | 每个周期的空闲步数,profiler 不采集 |
warmup | 预热步数,开始追踪但结果丢弃(消除 profiling 启动开销) |
active | 实际采集步数 |
repeat | 重复周期数(0 = 持续到结束) |
执行时序(以上述配置为例):
步骤: 0-9(skip) | 10-14(wait) | 15(warmup) | 16-18(active→trace_ready) | 19-23(wait) | 24(warmup) | 25-27(active→trace_ready)
with profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
) as prof:
model(inputs)
prof.export_chrome_trace("trace.json")
# 用浏览器打开 https://ui.perfetto.dev/ 或 chrome://tracing 加载 trace.json
with profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
with_stack=True,
) as prof:
model(inputs)
# group_by_stack_n=5 表示按调用栈前 5 帧分组
print(prof.key_averages(group_by_stack_n=5).table(
sort_by="self_cuda_time_total", row_limit=10
))
# 导出火焰图格式的栈信息
# prof.export_stacks("profiler_stacks.txt", metric="self_cuda_time_total")
from torch.profiler import record_function
def train_step(batch):
with record_function("data_preprocess"):
inputs, labels = preprocess(batch)
with record_function("forward"):
outputs = model(inputs)
with record_function("loss_compute"):
loss = criterion(outputs, labels)
with record_function("backward"):
loss.backward()
with record_function("optimizer_step"):
optimizer.step()
optimizer.zero_grad()
record_function 会在 profiler 输出和 trace 中显示为独立的标记区域,帮助快速定位各阶段耗时。
with profile(
activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA],
) as prof:
code_to_profile_0()
# 临时关闭 CUDA 采集
prof.toggle_collection_dynamic(False, [ProfilerActivity.CUDA])
code_to_skip()
# 重新开启 CUDA 采集
prof.toggle_collection_dynamic(True, [ProfilerActivity.CUDA])
code_to_profile_1()
分析 profiler 输出时,关注以下模式:
| 瓶颈模式 | 诊断信号 | 优化建议 |
|---|---|---|
| DataLoader 过慢 | enumerate(DataLoader) 占比大,GPU idle 时间长 | 设置 num_workers=4~8,pin_memory=True,persistent_workers=True |
| CPU-GPU 同步阻塞 | CPU 时间远大于 CUDA 时间,出现 cudaStreamSynchronize | 避免 tensor.item()、print(cuda_tensor)、.cpu() 调用 |
| 显存碎片化 / OOM | 显存分配量大,aten::empty 频繁 | 启用 gradient checkpointing,减小 batch size,预分配显存 |
| 小 kernel 启动开销 | 大量小 CUDA kernel,每个耗时 < 10us | 使用 torch.compile 做算子融合,或启用 CUDA Graph |
| 未使用 Tensor Core | matmul 算子 FLOPS 低于理论峰值 | 确认输入维度为 8 的倍数,启用 AMP (BF16/FP16) |
| 卷积未选最优算法 | conv 算子耗时异常 | 设置 torch.backends.cudnn.benchmark = True |
| 通信等待 | nccl:all_reduce 等通信算子占比大 | 启用通信计算重叠 (overlap_comm),检查负载均衡 |
| 逐元素操作瓶颈 | 大量 aten::add、aten::mul 等 pointwise 操作 | 用 torch.compile 自动融合,或 channels_last 内存格式 |
| 指标 | 健康值 | 说明 |
|---|---|---|
| GPU 利用率 | > 80% | < 50% 说明存在严重瓶颈 |
| Tensor Core 使用率 | > 50%(有 TC 的 GPU) | 0% 说明未启用混合精度或维度不对齐 |
| DataLoader 耗时占比 | < 10% | > 30% 说明数据管线是瓶颈 |
| 通信耗时占比(多卡) | < 20% | > 40% 需优化通信或检查负载均衡 |
| CPU-GPU 同步次数 | 尽量少 | 每个 step 不应超过 2-3 次必要同步 |
根据 profiler 输出指标,将瓶颈归类到以下类型,以指导后续优化方向:
| 瓶颈类型 | 诊断条件 | 典型 profiler 特征 | 优化方向 |
|---|---|---|---|
| Compute-bound | GPU 利用率高、吞吐远低于理论峰值 | CUDA kernel 持续执行无 gap,但 Self CUDA 时间长;matmul/conv 等计算算子占比 > 70% | 混合精度(BF16/FP16)、Tensor Core 对齐(维度为 8 的倍数)、torch.compile |
| Memory-bound | 大量 element-wise 操作、显存分配频繁 | aten::add、aten::mul、aten::copy_ 等 pointwise 算子占比高;aten::empty/aten::to 频繁出现 | 算子融合(Liger Kernel/torch.compile)、减少中间张量、Flash Attention |
| Launch-bound | 大量极短 kernel | 平均 Self CUDA < 10μs 的 kernel 数量 > 50%;CPU total 远大于 CUDA total | CUDA Graph、torch.compile、persistent kernel、减少 Python 调用 |
| Data I/O-bound | DataLoader 占比高 | enumerate(DataLoader) 或数据预处理算子在 CPU 时间排行靠前;GPU 周期性 idle | num_workers、pin_memory、prefetch_factor、数据格式优化 |
| Communication-bound | 通信算子占比高(多卡场景) | nccl:all_reduce、nccl:all_gather 占 CUDA 时间 > 30%;通信与计算未重叠 | overlap_comm、梯度压缩、ZeRO++、更高效并行策略 |
| Sync-bound | CPU-GPU 频繁同步 | cudaStreamSynchronize、cudaDeviceSynchronize 在 CPU 排行靠前;CPU wait 时间长 | 去掉 .item()、print(cuda_tensor)、延迟同步到 step 结束 |
按以下格式向用户呈现结果:
# PyTorch 性能分析报告
## 环境信息
- PyTorch 版本:X.Y.Z
- GPU:<型号> x <数量>
- 分析范围:<代码段描述>
## Top 10 耗时算子
| 排名 | 算子 | Self CUDA | CUDA Total | 调用次数 | 备注 |
|------|------|-----------|------------|---------|------|
| 1 | ... | ... | ... | ... | ... |
## GPU 利用率概览
- GPU 活跃时间占比:X%
- Tensor Core 使用情况:是/否
- 主要空闲原因:<DataLoader/CPU同步/通信等待>
## 显存分析(如启用)
- 峰值显存占用:X GB
- 主要显存消耗算子:...
- 是否存在碎片化:...
## 瓶颈诊断
1. **[严重程度]** <瓶颈描述> — 影响:<说明>
2. ...
## 优化建议(按优先级)
1. **[高]** <建议> — 预期收益:<说明>
```python
# 代码示例
---
### 附录:常见 sort_by 排序键
| 排序键 | 用途 |
|--------|------|
| `cpu_time_total` | CPU 总耗时 |
| `self_cpu_time_total` | CPU 自身耗时(不含子算子) |
| `cuda_time_total` | CUDA 总耗时 |
| `self_cuda_time_total` | CUDA 自身耗时 |
| `cpu_memory_usage` | CPU 内存分配总量 |
| `self_cpu_memory_usage` | CPU 内存自身分配量 |
| `cuda_memory_usage` | GPU 显存分配总量 |
| `self_cuda_memory_usage` | GPU 显存自身分配量 |
### 附录:可视化工具
| 工具 | 用途 | 使用方式 |
|------|------|----------|
| **Perfetto** | 查看 trace 时间线(推荐) | 导出 `trace.json`,在 https://ui.perfetto.dev/ 打开 |
| **Chrome Tracing** | 查看 trace 时间线 | 导出 `trace.json`,在 `chrome://tracing` 打开 |
| **TensorBoard** | 综合视图(已 deprecated 但仍可用) | `pip install torch_tb_profiler` + `tensorboard --logdir=./log` |
| **HTA (Holistic Trace Analysis)** | 分布式训练 trace 分析 | TensorBoard Profiler 的替代品 |
### 附录:PyTorch 性能调优速查清单
以下是从 PyTorch 官方性能调优指南中提取的关键优化项,可结合 profiler 结果逐项检查:
| 优化项 | 代码/配置 | 适用场景 |
|--------|----------|---------|
| 异步数据加载 | `DataLoader(num_workers=4~8, pin_memory=True)` | 所有训练任务 |
| 推理/验证时关闭梯度 | `with torch.no_grad():` | 验证/推理 |
| Conv+BN 去掉 bias | `nn.Conv2d(..., bias=False)` | Conv 后接 BatchNorm |
| 高效梯度清零 | `optimizer.zero_grad(set_to_none=True)` | 所有训练任务 |
| torch.compile 算子融合 | `model = torch.compile(model)` | PyTorch 2.x |
| channels_last 内存格式 | `model.to(memory_format=torch.channels_last)` | CV 模型 |
| Gradient Checkpointing | `torch.utils.checkpoint.checkpoint(fn, ...)` | 显存受限 |
| 关闭调试 API | 禁用 `detect_anomaly`、`emit_nvtx` 等 | 正式训练 |
| Tensor Core 精度控制 | `torch.set_float32_matmul_precision('high')` | 有 Tensor Core 的 GPU |
| CUDA Graph | `torch.compile(model, mode="reduce-overhead")` | 固定形状输入 |
| cuDNN 自动调优 | `torch.backends.cudnn.benchmark = True` | 卷积网络 |
| 避免 CPU-GPU 同步 | 不用 `.item()`、`print(cuda_tensor)` | 训练循环内 |
| 直接在 GPU 创建 tensor | `torch.rand(size, device='cuda')` | 减少 H2D 拷贝 |
| 混合精度训练 | `torch.amp.autocast('cuda', dtype=torch.bfloat16)` | 所有 GPU 训练 |
| 变长输入预分配显存 | 先用最大长度跑一遍 forward+backward | NLP/语音模型 |
| DDP 梯度累积时跳过 AllReduce | `with model.no_sync():` | 梯度累积 N-1 步 |
| NUMA 绑定(CPU 训练) | `numactl --cpunodebind=N --membind=N python train.py` | 多 socket CPU |
| 高效内存分配器 | `LD_PRELOAD=jemalloc.so` 或 `tcmalloc.so` | CPU 训练 |
| Tensor Core 维度对齐 | 确保 hidden_dim/vocab_size/head_dim 为 8 的倍数 | 所有使用 matmul 的模型 |
| 避免不必要的 .contiguous() | 只在 kernel 要求连续内存时调用 | 减少无意义的显存拷贝 |
| 使用 inplace 操作 | `F.relu(x, inplace=True)`、`tensor.add_(...)` | 减少中间张量分配 |
| 预分配 buffer 复用 | `torch.empty(..., out=buffer)` | 重复运算场景 |
| Memory-bound 检测 | 看 pointwise 算子占比,DRAM 吞吐 vs 峰值带宽比 | 判断是否值得做算子融合 |
| H2D/计算 overlap | 多 CUDA stream + `non_blocking=True` + prefetch | 数据传输与计算并行 |
### 附录:何时需要 Nsight Compute (ncu)
PyTorch Profiler 擅长 **算子级分析**(哪个算子慢、调用了多少次),但无法深入 kernel 内部。当你通过 profiler 定位到具体的热点 kernel 后,需要用 **Nsight Compute (ncu)** 分析 kernel 内部性能:
| 需要 ncu 的场景 | ncu 能给你的信息 |
|----------------|-----------------|
| 热点 kernel 优化方向不明确 | Roofline 模型判断 compute-bound vs memory-bound |
| matmul 性能未达预期 | SM 利用率、Tensor Core 命中率、pipeline stall |
| 自定义 Triton/CUDA kernel 调优 | achieved occupancy、register 使用量、shared memory bank conflict |
| 怀疑访存效率低 | L2 hit rate、DRAM throughput、global load efficiency |
| warp 级性能问题 | warp stall reason 分布、branch efficiency |
**使用流程**:PyTorch Profiler 定位热点 → nsys 看系统级时间线 → ncu 分析具体 kernel → 优化 → 验证。