写给前端的 CAAN-profiling-suite:昇腾性能分析套件到底是啥?

上周做模型优化,老板问我:“你的模型在 NPU 上跑得慢,到底慢在哪里?” 我愣住了——不知道啊。

好问题。今天一次说清楚。

profiling-suite 是啥?

profiling-suite 是昇腾的性能分析套件。采集 NPU 执行的方方面面,帮你找到性能瓶颈。

一句话说清楚:profiling-suite 是昇腾的性能分析套件,采集 NPU 执行数据、算子耗时、通信耗时、内存带宽,生成可视化报告。

你说气人不气人,之前找性能瓶颈靠猜,现在用 profiling-suite 直接看数据。

为什么需要 profiling-suite?

三种情况:

1. 模型慢,不知道为什么
算子是瓶颈?通信是瓶颈?内存带宽是瓶颈?

2. 优化后不知道有没有效
改了代码,性能有没有提升?提升了多少?

3. 想深入优化
看流水线、看内存访问模式、看指令发射效率。

profiling-suite 核心能力

1. 性能数据采集

采集 NPU 执行的各类数据。

import torch
import torch_npu
from canning import profiling_suite as ps

# 方式一:上下文管理器
with ps.Profile() as prof:
    output = model(input_data)

# 方式二:装饰器
@ps.profile()
def forward(model, input_data):
    return model(input_data)

output = forward(model, input_data)

# 方式三:手动控制
ps.start()
output = model(input_data)
ps.stop()

你说气人不气人,加一行代码就能采集完整性能数据。

2. 算子耗时分析

看每个算子花了多少时间。

import torch
import torch_npu
from canning import profiling_suite as ps

model = MyModel().npu()

with ps.Profile() as prof:
    output = model(input_data)

# 打印算子耗时
print(prof.op_summary())

# 输出:
# Op Type         Calls    Total(ms)   Avg(ms)    Min(ms)    Max(ms)
# ---------------------------------------------------------------------------
# MatMul         100       350.2        3.50        3.20        4.10
# Conv2D         50       210.5        4.21        3.80        5.20
# Softmax        100       15.3         0.15        0.12        0.18
# LayerNorm      100       25.7         0.26        0.22        0.30

结论:MatMul 和 Conv2D 是瓶颈,优化重点在这里。

3. 内存分析

看内存分配、释放、峰值。

import torch
import torch_npu
from canning import profiling_suite as ps

with ps.Profile() as prof:
    output = model(input_data)

# 打印内存报告
print(prof.memory_summary())

# 输出:
# Memory Events:
# - Allocations: 1,200
# - Frees: 1,150
# - Peak memory: 8.5 GB
# - Device: NPU:0

结论:峰值内存 8.5GB,如果 NPU 内存只有 16GB,要注意 OOM 风险。

4. 通信分析

看集合通信的耗时。

import torch
import torch_npu
import torch.distributed as dist
from canning import profiling_suite as ps

dist.init_process_group(backend='hccl')

with ps.Profile() as prof:
    output = model(input_data)
    dist.all_reduce(tensor)

# 打印通信报告
print(prof.communication_summary())

# 输出:
# Communication Events:
# - AllReduce: 50 calls, 120ms total, 2.4ms avg
# - AllGather: 20 calls, 80ms total, 4.0ms avg
# - ReduceScatter: 30 calls, 90ms total, 3.0ms avg

结论:AllReduce 通信占比 15%,考虑使用梯度累积或者通信-计算重叠。

5. 时间线导出

导出 Chrome Trace 格式,在 Chrome 里可视化。

import torch
import torch_npu
from canning import profiling_suite as ps

with ps.Profile() as prof:
    for i in range(10):
        output = model(input_data)

# 导出时间线
prof.export_chrome_trace("trace.json")

# 在 Chrome 中打开:
# 1. 打开 chrome://tracing/
# 2. 点击 "Load" 按钮
# 3. 选择 trace.json
# 4. 查看时间线可视化

时间线能看到的:

  • 每个算子的开始/结束时间
  • 算子之间的并行关系
  • 内存分配/释放事件
  • 通信事件

6. 性能建议

自动给出优化建议。

import torch
import torch_npu
from canning import profiling_suite as ps

with ps.Profile() as prof:
    output = model(input_data)

# 打印优化建议
print(prof.optimization_suggestions())

# 输出:
# Optimization Suggestions:
# 1. MatMul op consumes 35% of total time. Consider:
#    - Use FlashAttention for attention computation
#    - Enable operator fusion for MatMul + bias + activation
# 2. Communication overhead is 18%. Consider:
#    - Overlap communication with computation
#    - Use gradient accumulation
# 3. Memory peak is 8.5GB. Consider:
#    - Use activation checkpointing
#    - Use FP16 precision

性能数据

在昇腾 910 上分析 ResNet-50 推理:

分析项目 数值 说明
总耗时 15.2ms 单张图片推理
算子耗时占比 85% 计算是瓶颈
通信耗时占比 5% 可忽略
内存峰值 1.2GB 模型 + 中间激活值
最慢算子 Conv2D 占总时间 40%

优化后:

分析项目 数值 提升
总耗时 8.5ms 1.8x
算子耗时占比 90% -
通信耗时占比 3% -
内存峰值 0.8GB 1.5x 降低
最慢算子 Conv2D (fusion) 25% 占比

怎么用?

方式一:Python 接口(推荐)

import torch
import torch_npu
from canning import profiling_suite as ps

# 1. 准备模型和数据
model = ResNet50().npu()
input_data = torch.randn(1, 3, 224, 224).npu()

# 2. 性能分析
with ps.Profile(
    activities=[ps.ProfilerActivity.CPU, ps.ProfilerActivity.NPU],
    record_shapes=True,
    profile_memory=True,
    with_stack=True
) as prof:
    output = model(input_data)

# 3. 查看报告
print(prof.key_averages().table(sort_by="cpu_time_total", row_limit=10))

# 4. 导出时间线
prof.export_chrome_trace("resnet50_trace.json")

方式二:命令行工具

# 分析脚本
canning-prof --mode=performance \
            --output=./prof_results \
            python train.py

# 查看报告
canning-prof --mode=view \
            --input=./prof_results/performance.json

方式三:集成到训练脚本

import torch
import torch_npu
from canning import profiling_suite as ps

# 只在特定 step 分析,避免大量日志
prof = ps.Profile(
    start_on_cpu=True,
    with_stack=True,
    record_shapes=True
)

for epoch in range(num_epochs):
    for step, (inputs, labels) in enumerate(dataloader):
        inputs = inputs.npu()
        labels = labels.npu()

        # 每 100 个 step 分析一次
        if step % 100 == 0:
            prof.start()

        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        if step % 100 == 0:
            prof.stop()
            prof.export_chrome_trace(f"trace_step_{step}.json")
            prof.start()  # 重置,开始新的采集

prof.stop()

踩坑指南(亲身经历)

  1. 分析会影响性能

    • 开启 profiling 会增加 5-15% 开销
    • 不要在每个 step 都开启
    • 定期采样即可
  2. 时间线文件很大

    • 长时间采集会生成 GB 级 JSON
    • 设置 with_stack=False 可以减少文件大小
    • 或者只采集特定 step
  3. 内存分析不准

    • 如果用了 torch_npu.npu.empty_cache(),内存分析会不准
    • 建议在分析时注释掉 empty_cache()
  4. 分布式训练要指定 rank

    prof = ps.Profile(
        activities=[ps.ProfilerActivity.NPU],
        on_trace_ready=ps.tensorboard_trace_handler('./log'),
        record_shapes=True
    )
    

与 msprof 的区别

特性 profiling-suite msprof
定位 Python 层性能分析 系统层性能分析
使用难度 简单(Python API) 中等(命令行 + 配置)
粒度 算子级 算子级 + 指令级
输出格式 Python 对象 + Chrome Trace JSON + 文本报告
适用场景 快速定位瓶颈 深度性能调优

简单说:

  • profiling-suite:Python 开发者,快速定位瓶颈
  • msprof:系统工程师,深度性能调优

总结

profiling-suite 就是昇腾的性能分析套件:

  • 采集算子耗时
  • 采集内存使用
  • 采集通信耗时
  • 导出时间线可视化
  • 给出优化建议
Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐