前言

西安某高校的AI实验室,几个研究生围着一块Atlas 200 DK开发板发愁。

导师布置的任务:用昇腾NPU跑一个目标检测Demo,下周组会汇报。

GitHub上搜了一圈,算子文档看不懂,API手册太厚,示例代码要么太简单(只有矩阵加法)要么太复杂(直接上YOLOv8,2000行代码)。

"有没有那种……刚刚好的示例代码?"一个学生嘀咕。

导师推门进来,扔过来一个链接:

https://atomgit.com/cann/cann-samples

“看这个,官方示例,从Hello World到YOLO,从单算子到端到端,都有。”

一周后,组会汇报。那几个学生不仅跑通了YOLOv8,还把NPU和GPU的性能对比做出来了。

cann-samples,就是那个"刚刚好"的东西。


一、cann-samples是什么?

cann-samples是昇腾CANN开源社区的官方示例代码库

说人话:它是CANN的"菜谱"——你要做哪道菜(哪种任务),这里都有对应的"步骤说明+代码"(示例代码)。

打个比方:

  • CANN文档 = 字典(每个字的意思都有,但不知道怎么造句)
  • cann-samples = 作文范文(不同类型的文章都有范例)

仓库地址:https://atomgit.com/cann/cann-samples

目录结构(核心部分):

cann-samples/
├── operator/          # 算子开发示例(Custom OP)
├── inference/         # 推理示例(模型部署)
├── training/          # 训练示例(分布式训练)
├── performance/       # 性能优化示例
├── multimedia/        # 多媒体处理示例(视频解码等)
└── README.md          # 索引文档(每个示例的说明)

二、为什么需要cann-samples?

CANN的文档很全,但有一个问题:太碎了

你想做一个"用NPU跑YOLOv8推理"的任务,需要了解:

  1. 怎么把PyTorch模型转成OM(ATC工具)
  2. 怎么用AscendCL加载OM模型
  3. 怎么预处理输入图片(AIPP or 手动)
  4. 怎么解析输出张量(后处理)
  5. 怎么测性能(msprof工具)

文档里,这5步分散在5个不同的页面。你得自己拼。

cann-samples把这些都拼好了。 一个示例目录 = 一个完整可运行的任务。


三、核心示例详解

1. operator/custom_op:自定义算子

这是最常用的示例——教你如何在CANN里添加自己的算子(不在官方算子库里的)。

目录结构:

operator/custom_op/
├── README.md          # 详细说明
├── op_host/           # 算子Host侧代码(Tiling计算)
├── op_kernel/         # 算子Kernel侧代码(NPU执行)
├── test/              # 单元测试
└── run.sh             # 一键编译运行脚本

核心步骤(简化版):

Step 1:写算子原型(op_host)

// custom_op_host.cpp
#include "operator_host.h"
void CustomOpHost(gert::TilingContext* ctx) {
    // 计算Tiling参数(Block维度、Vector核分配等)
    // 把结果写到ctx->GetTilingData()
}

Step 2:写算子Kernel(op_kernel)

// custom_op_kernel.cpp
extern "C" __global__ __aicore__ void CustomOpKernel(
    uint8_t* input, uint8_t* output, TilingData* tiling) {
    // 在NPU的Vector核上执行
    // 使用Ascend C编程范式
    ...
}

Step 3:编译+测试

bash run.sh
# 自动完成:编译 → 生成算子包 → 安装 → 运行单元测试

⚠️ 踩坑点:Tiling计算是算子开发的核心难点。cann-samples里的示例有详细的Tiling注释,照着改就行。如果自己写,很容易算出错的Block分配(导致NPU利用率不到30%)。

2. inference/yolov8:目标检测推理

这是最受欢迎的示例——完整的YOLOv8推理流程,从模型转换到后处理,一条龙。

目录结构:

inference/yolov8/
├── README.md
├── model/             # YOLOv8的ONNX模型 + ATC转换脚本
├── src/
│   ├── main.cpp       # 主程序
│   ├── preprocess.cpp # 图片预处理(Resize + BGR2RGB + Normalize)
│   ├── infer.cpp      # 推理执行(AscendCL API调用)
│   └── postprocess.cpp# 后处理(NMS + 画框)
├── build.sh           # 编译脚本
└── run.sh             # 运行脚本

关键代码片段(推理部分):

// infer.cpp
aclmdlDataset* input = aclmdlCreateDataset();
aclmdlDataset* output = aclmdlCreateDataset();

// 加载模型
uint32_t modelId;
aclmdlLoadFromFile("yolov8.om", &modelId);

// 准备输入(从preprocess来)
aclmdlAddDatasetBuffer(input, inputDevBuf, inputSize);

// 执行推理
aclmdlExecute(modelId, input, output);

// 获取输出(给postprocess)
aclDataBuffer* outBuf = aclmdlGetDatasetBuffer(output, 0);
void* outData = aclGetDataBufferAddr(outBuf);

性能数据(Atlas 800T A2,YOLOv8-s):

  • PyTorch(GPU A100):8.3 ms/frame
  • cann-samples(NPU):7.1 ms/frame
  • 加速比:1.17x

3. training/distributed:分布式训练

教你用hccl做多卡分布式训练(数据并行)。

目录结构:

training/distributed/
├── README.md
├── single_card.py     # 单卡训练(基准)
├── data_parallel.py   # 数据并行(多卡)
├── model_parallel.py  # 模型并行(超大模型)
└── run_distributed.sh # 启动脚本(调用hccl)

关键代码(数据并行):

# data_parallel.py
import torch
import torch_npu
import torch.distributed as dist

# 初始化hccl
dist.init_process_group(backend='hccl')

# 模型放到NPU
model = MyModel().npu()

# 包装为DDP(DistributedDataParallel)
model = torch.nn.parallel.DistributedDataParallel(model)

# 训练循环(和单卡一样)
for epoch in range(num_epochs):
    for batch in dataloader:
        loss = model(batch)
        loss.backward()
        optimizer.step()

启动命令:

bash run_distributed.sh 8  # 8卡训练

性能数据(ResNet-50,ImageNet,8x Atlas 800T A2):

  • 单卡:1800 images/s
  • 8卡数据并行:13800 images/s
  • 扩展效率:96%(接近线性扩展)

4. performance/profiling:性能分析

教你用msprof工具分析NPU程序的性能瓶颈。

目录结构:

performance/profiling/
├── README.md
├── sample_app.cpp     # 示例程序(故意留了性能问题)
├── run_profiler.sh    # 运行msprof采集性能数据
└── analyze_report.py  # 解析msprof输出,生成分析报告

使用流程:

# Step 1:用msprof运行程序(自动采集性能数据)
msprof --application=./sample_app --output=./prof_data

# Step 2:解析性能数据
python analyze_report.py ./prof_data

# 输出:
# - 算子执行时间占比(哪几个算子最慢)
# - 显存带宽利用率(是否显存带宽瓶颈)
# - Cube Unit利用率(是否算力瓶颈)
# - 建议优化方向(算子融合/量化/多Stream等)

实战价值:这个示例能帮你快速定位性能瓶颈。比如,如果报告显示"Cube Unit利用率 < 30%",说明你的程序是显存带宽瓶颈,应该做算子融合或量化,而不是加卡。


四、cann-samples的"隐藏宝藏"

除了上面的核心示例,cann-samples里还有一些"冷门但有用"的示例:

1. multimedia/vdec:视频解码

教你用NPU的硬件解码单元(VDEC)做视频解码,比用CPU的OpenCV快10倍。

// 初始化VDEC通道
vdecChannel = aclvdecCreateChannel(0);

// 喂H.264码流
aclvdecSendFrame(vdecChannel, h264Data, frameSize);

// 接收解码后的YUV帧
aclvdecGetFrame(vdecChannel, &yuvFrame);

2. inference/zero_copy:零拷贝推理

教你用零拷贝技术(Host和Device共享内存)减少数据搬运开销。

关键:用aclrtMallocHost分配锁页内存,NPU可以直接访问(不需要Memcpy)。

性能提升:推理延迟降低15-20%(主要省掉了Host→Device的Memcpy时间)。

3. operator/debug:算子调试

教你用AscendCL的调试工具(printf、dump张量等)调试自定义算子。

// 在Kernel代码里打印调试信息
printf("block_idx=%d, input[0]=%f\n", block_idx, input[0]);

// Dump张量到文件(用msprof)
msprof --dump-data=true --application=./my_op

五、cann-samples在CANN生态里的位置

上层应用(你的代码)
    ↓
cann-samples(示例代码,教你怎么做)
    ↓
CANN各组件(ops-transformer、ATB、hccl、ge、Runtime等)
    ↓
NPU硬件

cann-samples是"桥梁"——它连接了"文档"(告诉你每个API是什么)和"实战"(告诉你怎么把API组合起来完成任务)。

没有cann-samples,你得自己试错(踩坑无数)。
有了cann-samples,你直接抄作业(改改就能用)。


六、那些你可能想问的问题

Q1:cann-samples的示例能直接用在生产环境吗?

A:部分可以(如inference/yolov8),部分需要改造(如operator/custom_op,只是演示Tiling怎么写,实际算子要优化性能)。建议把cann-samples当"模板",而不是"成品"。

Q2:cann-samples支持最新版的CANN吗?

A:通常滞后1-2个版本。如果用了新版CANN的API,示例可能编译不过。解决方法:看示例的README,里面有"适用CANN版本"说明。

Q3:我想贡献自己的示例代码,怎么提交?

A:Fork仓库 → 按目录结构添加示例(要有README + 完整可运行代码)→ 提交PR。官方会审核代码质量和文档完整性。

Q4:cann-samples和modelzoo有什么区别?

A:cann-samples是"示例代码"(教你怎么做),modelzoo是"预训练模型"(直接下载就能用)。比如,cann-samples里有"怎么把PyTorch模型转OM"的示例,modelzoo里有"已经转好的OM模型"。


结尾

回到开头那个故事。

西安那个实验室的学生,后来给cann-samples提了个PR——添加了一个"用NPU做视频目标检测"的示例(结合multimedia/vdec和inference/yolov8)。

PR被合并了。他们的名字,现在还留在cann-samples的Contributors列表里。

导师在组会上说:“最好的学习方法,就是把你踩过的坑,写成示例,让后来人别再踩。”

cann-samples就是这样一个地方——万法归宗,从Hello World到生产级代码,都有迹可循。

仓库地址(纯URL):

https://atomgit.com/cann/cann-samples

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐