趋近智
了解模型在执行过程中时间与资源的分配情况,是进行优化的根本。在应用量化或剪枝等技术之前,你需要找出性能瓶颈所在。是CPU限制了性能?是GPU未充分利用?还是特定操作过慢?PyTorch Profiler (torch.profiler) 是回答这些问题的标准工具。
该分析器使你能够查看模型执行不同部分的时间和内存开销,包括CPU上的Python操作和GPU上的CUDA内核执行。它提供详细的视图,帮助你进行优化,确保你把精力放在那些能为推理带来最大性能提升的方面。
torch.profiler API 通过跟踪几个重要指标,提供模型执行的全面视图:
cudaMemcpy)隐式显示,突出显示在主机(CPU)和设备(GPU)之间移动数据所花费的时间。使用分析器最常见的方式是通过其上下文管理器接口。你将要分析的代码段包装在 with torch.profiler.profile(...) 块中。
import torch
import torchvision.models as models
from torch.profiler import profile, record_function, ProfilerActivity
# 加载预训练模型(确保其处于评估模式以进行推理分析)
model = models.resnet18().cuda().eval()
inputs = torch.randn(16, 3, 224, 224).cuda() # GPU上的示例输入批次
# 基本分析上下文
with profile(activities=[ProfilerActivity.CPU, ProfilerActivity.CUDA], record_shapes=True) as prof:
with record_function("model_inference"): # 该代码块的可选标签
model(inputs)
# 打印汇总统计信息
print(prof.key_averages().table(sort_by="cuda_time_total", row_limit=10))
# 导出结果以进行更详细的分析
# prof.export_chrome_trace("resnet18_trace.json")
# prof.export_stacks("/tmp/profiler_stacks.txt", "self_cuda_time_total")
让我们分解 profile() 中使用的参数:
activities: 一个列表,指定要分析的活动。常见选择是 ProfilerActivity.CPU 和 ProfilerActivity.CUDA。分析CUDA活动对于了解GPU性能很重要。record_shapes: 如果为 True,则记录被分析运算符的输入形状。这对于诊断与形状相关的性能问题有用,但会增加一些开销。profile_memory: 如果为 True,则启用内存分析(分配/释放)。开销较大。with_stack: 如果为 True,则记录Python调用堆栈。对于将运算符回溯到源代码很有用,但开销很大。on_trace_ready: 一个可调用对象(通常是 torch.profiler.tensorboard_trace_handler),用于处理结果导出,例如直接导出到TensorBoard。schedule: 控制长时间运行作业的分析持续时间。使用 torch.profiler.schedule(wait, warmup, active, repeat) 定义阶段:跳过初始 wait 步,执行 warmup 步(分析器活动但结果被丢弃),记录 active 步,并重复此循环 repeat 次。这对于排除初始化开销并专注于稳态性能很有用。record_function("label") 上下文管理器将自定义标签添加到分析器输出中,使识别代码中特定逻辑块(如数据预处理、模型前向传播、后处理)变得更容易。
分析器对象(示例中的 prof)提供了几种分析收集数据的方法:
key_averages()此方法返回运算符性能的汇总摘要,该摘要在分析窗口内取平均。调用 .table() 提供格式化的字符串输出。
# Example Output Snippet from prof.key_averages().table(...)
------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------
Name Self CPU % Self CPU CPU total % CPU total CUDA % CUDA total # Calls
------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------
aten::convolution 0.14% 293.164us 17.21% 35.96ms 81.90% 35.91ms 20
aten::cudnn_convolution 0.00% 0.000us 0.00% 0.000us 81.72% 35.83ms 20
aten::addmm 0.06% 117.880us 1.12% 2.34ms 4.97% 2.18ms 1
aten::mm 0.00% 0.000us 0.00% 0.000us 4.96% 2.18ms 1
aten::add_ 0.13% 263.820us 0.51% 1.07ms 3.48% 1.53ms 21
aten::relu 0.12% 245.750us 0.28% 577.870us 1.91% 836.370us 16
aten::_native_batch_norm_legit_no_... 0.10% 215.530us 1.99% 4.15ms 1.85% 810.318us 20
aten::empty_strided 1.76% 3.68ms 1.94% 4.05ms 0.00% 0.000us 140
aten::max_pool2d_with_indices 0.04% 79.630us 0.38% 788.380us 0.79% 346.077us 1
aten::copy_ 0.06% 120.600us 0.06% 120.600us 0.00% 0.000us 2
------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------
Self CPU time total: 208.96ms
Self CUDA time total: 44.08ms
aten::convolution)。aten 是PyTorch原生C++运算符的命名空间。aten::convolution 调用 aten::cudnn_convolution)。你可以对表格进行排序(例如,sort_by="cuda_time_total")并限制行数(row_limit),以便关注最耗时的操作。按输入形状(group_by_input_shape=True)或堆栈跟踪(group_by_stack_n)分组可以提供更多信息。像 aten::convolution 这样的运算符如果 Self CUDA 时间很高,表明底层的CUDA卷积内核花费了大量时间,这通常是预期的,但能确认GPU时间用在了何处。高 Self CPU 时间可能指向Python开销或CPU密集型计算。
export_chrome_trace()此方法将详细的时间线数据导出为JSON文件格式,该格式与Chrome的跟踪工具(chrome://tracing)或Perfetto UI(推荐)兼容。这种可视化对于理解模型的时间动态很有用。
查看跟踪:打开Google Chrome,导航到
chrome://tracing,然后点击“加载”,或者使用Perfetto UI:ui.perfetto.dev。
跟踪视图通常显示:
Stream 7),显示在GPU上计划的内核执行和内存传输。CPU启动GPU内核的简化视图。Chrome跟踪提供详细的时间线视图,显示精确的开始/结束时间以及可能表示空闲期的间隔。
通过检查跟踪,你可以发现:
memcpy 块显示数据移动所花费的时间。使用 torch.profiler.tensorboard_trace_handler 可在TensorBoard中提供集成体验。
import torch
import torchvision.models as models
from torch.profiler import profile, tensorboard_trace_handler
# 模型和输入设置(同前)
model = models.resnet18().cuda().eval()
inputs = torch.randn(16, 3, 224, 224).cuda()
log_dir = "./logs" # TensorBoard日志目录
with profile(activities=[torch.profiler.ProfilerActivity.CPU, torch.profiler.ProfilerActivity.CUDA],
profile_memory=True, # 可选地跟踪内存
on_trace_ready=tensorboard_trace_handler(log_dir)) as prof:
model(inputs)
print(f"Profiler results saved to {log_dir}. Run: tensorboard --logdir {log_dir}")
启动TensorBoard(tensorboard --logdir ./logs)并导航到“PyTorch Profiler”选项卡,提供多种交互式视图:
key_averages,显示每个运算符的详细统计信息。允许过滤和搜索。profile_memory=True)显示每个运算符的内存使用模式和分配情况。总GPU时间在不同运算符上的分布示例,源于分析器数据。在CNN中,卷积通常占用大部分时间。
分析器输出直接指向优化机会:
key_averages 中总CPU时间高,Python开销大或某些运算符的自身CPU时间长。跟踪视图中GPU利用率低(大间隔)。num_workers,pin_memory)。cudaMemcpyDtoH(设备到主机)或 cudaMemcpyHtoD(主机到设备)这样的操作花费了大量时间。DataLoader 中使用 pin_memory=True,并在 .to(device) 调用中使用 non_blocking=True 以实现传输和计算的重叠。profile_memory=True 报告的峰值内存使用量高。执行期间出现 OutOfMemoryError。torch.no_grad(),删除不再需要的张量(del tensor),使用检查点技术(以计算换内存),应用模型优化技术如量化或剪枝(本章其他部分涵盖),减小批次大小。aten::convolution 这样的标准内核)在 key_averages 或跟踪视图中显示非常长的执行时间。with torch.profiler.record_function("my_label"): 向分析结果添加自定义注释,从而更容易将性能数据与代码的特定部分(例如,“data_preprocessing”、“attention_block”)关联起来。torch.profiler.profile 的 schedule 参数,以便在初始预热期后捕获特定迭代,避免生成过大的跟踪文件,并专注于稳态行为。通过系统地使用PyTorch Profiler,你将对模型的运行时行为获得必要的了解,以便明智地决定在哪里以及如何有效地应用优化技术,最终得到更快速、更高效的模型,为部署做好准备。
这部分内容有帮助吗?
torch.profiler API 的官方文档,详细说明其功能、配置选项和与其他工具的集成。© 2026 ApX Machine Learning用心打造