昇腾CANN的“万法归宗“:cann-samples仓库探秘
西安某高校的AI实验室,几个研究生围着一块Atlas 200 DK开发板发愁。导师布置的任务:用昇腾NPU跑一个目标检测Demo,下周组会汇报。GitHub上搜了一圈,算子文档看不懂,API手册太厚,示例代码要么太简单(只有矩阵加法)要么太复杂(直接上YOLOv8,2000行代码)。"有没有那种……刚刚好的示例代码?"一个学生嘀咕。导师推门进来,扔过来一个链接:“看这个,官方示例,从Hello W
前言
西安某高校的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推理"的任务,需要了解:
- 怎么把PyTorch模型转成OM(ATC工具)
- 怎么用AscendCL加载OM模型
- 怎么预处理输入图片(AIPP or 手动)
- 怎么解析输出张量(后处理)
- 怎么测性能(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
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐



所有评论(0)