昇腾CANN cann-samples 仓:从示例代码入手理解昇腾NPU编程模型
这篇文章介绍了如何使用昇腾CANN官方示例库cann-samples快速上手NPU开发。主要内容包括: 环境准备:安装CANN Toolkit和NPU驱动,配置开发环境 示例分类:介绍cann-samples包含的四种核心示例类型 单算子调用示例:详细展示了如何使用AscendCL调用加法算子,包括初始化、数据准备、算子执行和结果获取的完整流程 模型推理示例:演示如何将ONNX模型转换为OM格式并
·
前言
你刚装好 CANN,打开官方文档,满屏的 AscendCL、Runtime、GE、DVPP…不知道从哪下手。cann-samples 是 CANN 的官方示例代码库,从最简单的初始化到复杂的分布式训练都有。
这篇文章手把手带你跑通几个核心示例,理解昇腾 NPU 的编程模型。
cann-samples 的定位
cann-samples 在 GitHub 上叫 cann-samples,是 CANN 官方的示例代码集合。
# 克隆仓库
git clone https://atomgit.com/cann/cann-samples.git
cd cann-samples
tree -L 2
# 输出:
# cann-samples/
# ├── common/ # 公共接口
# │ ├── acllite_utils.h # AscendCL 工具
# │ ├── utils.h # 通用工具
# │ └── base/
# ├── inc/ # 头文件
# │ ├── CMakeLists.txt
# │ └── ascendcl/
# ├── src/ # 源代码
# ├── sample/ # 示例代码
# │ ├── acllite_sample/ # AscendCL 示例
# │ ├── model_infer_sample/ # 模型推理示例
# │ ├── dvpp_sample/ # DVPP 示例
# │ └── dist_train_sample/ # 分布式训练示例
# └── scripts/ # 编译脚本
示例分类:
- AscendCL 单算子调用(最简单,适合入门)
- 图推理(ONNX → OM 推理)
- DVPP 视频预处理(摄像头/视频流接入)
- 分布式训练(多卡训练)
环境准备
1. 安装 CANN Toolkit
# 安装 CANN Toolkit(包含 AscendCL、ATC、Runtime)
wget https://ascend-repo.obs.cn-north-4.myhuaweicloud.com/CANN/5.0.RC3/Ascend-cann-toolkit_5.0.RC3_linux-x86_64.run
sudo bash Ascend-cann-toolkit_5.0.RC3_linux-x86_64.run --install
# 配置环境变量
source /usr/local/Ascend/cann-toolkit/setenv.sh
2. 安装 CANN Driver
# 安装 NPU 驱动(必要!如果还没装)
wget https://ascend-repo.obs.cn-north-4.myhuaweicloud.com/driver/AscendDriver-5.0.RC3.zip
sudo ./AscendDriver-5.0.RC3-linux-x86_64.run --full
3. 验证安装
# 检查 NPU 设备
npu-smi info
# 应该看到 NPU 设备信息
# 检查 AscendCL
which ascendc
# 应该输出路径
4. 准备测试数据
# 下载测试图像(用于 DVPP 示例)
wget https://ascend-repo.obs.cn-north-4.myhuaweicloud.com/dataset/test_image_1080p.jpg
# 下载测试模型(用于推理示例)
wget https://ascend-repo.obs.cn-north-4.myhuaweicloud.com/model/resnet50.onnx
示例1:AscendCL 单算子调用(最简单)
这个示例演示如何用 AscendCL 调用一个简单的加法算子。
步骤1:写代码
// src/acllite_sample/add_sample.c
#include "acllite/acllite.h"
// 主函数
int main(int argc, char *argv[]) {
// 1. 初始化 AscendCL(必须的第一步)
aclliteInit(ACLLITE_BACKEND_DEFAULT);
// 2. 创建输入数据(两个向量)
const int dataSize = 10;
float inputX[dataSize] = {1.0, 2.0, 3.0, 4.0, 5.0,
6.0, 7.0, 8.0, 9.0, 10.0};
float inputY[dataSize] = {0.1, 0.2, 0.3, 0.4, 0.5,
0.6, 0.7, 0.8, 0.9, 1.0};
// 3. 创建 Device 侧的 Tensor
aclliteTensor *tensorX = aclliteCreateTensor(dataSize, ACLLITE_DATA_TYPE_FLOAT32);
aclliteTensor *tensorY = aclliteCreateTensor(dataSize, ACLLITE_DATA_TYPE_FLOAT32);
aclliteTensor *tensorZ = aclliteCreateTensor(dataSize, ACLLITE_DATA_TYPE_FLOAT32);
// 4. 拷贝数据到 Device(Host → NPU)
aclliteSetTensorData(tensorX, inputX, dataSize * sizeof(float));
aclliteSetTensorData(tensorY, inputY, dataSize * sizeof(float));
// 5. 执行加法算子(AscendCL 内置算子)
aclliteOpExecute("add", tensorX, tensorY, tensorZ);
// 6. 同步等待执行完成
aclliteSynchronize();
// 7. 读取结果(Device → Host)
float output[dataSize];
aclliteGetTensorData(tensorZ, output, dataSize * sizeof(float));
// 8. 打印结果
printf("Result: ");
for (int i = 0; i < dataSize; i++) {
printf("%.1f ", output[i]);
}
printf("\n");
// 输出:Result: 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 11.0
// 9. 释放资源
aclliteDestroyTensor(tensorX);
aclliteDestroyTensor(tensorY);
aclliteDestroyTensor(tensorZ);
// 10. 退出 AscendCL(必须的最后一步)
aclliteFinalize();
return 0;
}
步骤2:编译
# 编译
cd cann-samples
./scripts/build.sh --clean
# 或者手动编译
gcc -o add_sample src/acllite_sample/add_sample.c \
-I./inc -L/usr/local/Ascend/cann-toolkit/lib64 \
-lascend_cl -lpthread -ldla -lm -Wl,-rpath=/usr/local/Ascend/cann-toolkit/lib64
# 输出:add_sample(可执行文件)
步骤3:运行
# 运行
./add_sample
# 输出:Result: 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 11.0
这个示例的核心要点:
- 先
aclliteInit()初始化 - 创建 Tensor(输入、输出)
- 拷贝数据到 Device
- 调用算子
- 同步等待
- 读取结果
aclliteFinalize()退出
示例2:图推理(ONNX → OM)
这个示例演示如何把 ONNX 模型转成 OM 离线模型,然后在昇腾 NPU 上推理。
步骤1:用 ATC 转换模型
# ATC(Ascend Tensor Compiler)把 ONNX 转成 OM
atc --model=resnet50.onnx \
--framework=5 \
--output=resnet50 \
--soc_version=Ascend910B \
--input_shape="input:1,3,224,224" \
--input_format=NCHW \
--output_type=FP16
# 输出:resnet50.om(离线模型文件)
ATC 关键参数:
--model:输入模型(ONNX、TensorFlow、PyTorch)--framework:输入框架(1=CAFFE,3=TensorFlow,5=ONNX,6=PyTorch)--output:输出模型名--soc_version:目标芯片型号(Ascend910B、Ascend310B、Ascend310P)--input_shape:输入 Shape(固定 batch、动态分别用-1)
步骤2:写推理代码
// src/model_infer_sample/resnet50_infer.c
#include "acllite/acllite.h"
int main(int argc, char *argv[]) {
// 1. 初始化
aclliteInit(ACLLITE_BACKEND_DEFAULT);
// 2. 加载 OM 模型
aclliteModel *model = aclliteLoadModel("resnet50.om");
if (model == NULL) {
printf("Failed to load model\n");
return -1;
}
// 3. 准备输入(读取测试图像)
aclliteTensor *input = aclliteCreateTensorByImage(
"test_image_1080p.jpg",
1, 3, 224, 224,
ACLLITE_DATA_TYPE_FLOAT32
);
// 4. 创建输出
aclliteTensor *output = aclliteCreateTensor(1000, ACLLITE_DATA_TYPE_FLOAT32);
// 5. 推理
aclliteInference(model, input, output);
aclliteSynchronize();
// 6. 解析结果(Top-5 分类)
float *probs = (float *)accliteGetTensorDataPtr(output);
int top5[5];
float top5_prob[5];
for (int i = 0; i < 5; i++) {
top5_prob[i] = -1.0;
for (int j = 0; j < 1000; j++) {
if (probs[j] > top5_prob[i]) {
top5_prob[i] = probs[j];
top5[i] = j;
}
}
probs[top5[i]] = -1.0; // 排除已选的
}
// 7. 打印结果
printf("Top-5 predictions:\n");
for (int i = 0; i < 5; i++) {
printf(" %d: %.3f\n", top5[i], top5_prob[i]);
}
// 8. 释放资源
aclliteDestroyTensor(input);
aclliteDestroyTensor(output);
aclliteUnloadModel(model);
aclliteFinalize();
return 0;
}
步骤3:编译和运行
# 编译
gcc -o resnet50_infer src/model_infer_sample/resnet50_infer.c \
-I./inc -L/usr/local/Ascend/cann-toolkit/lib64 \
-lascend_cl -lpthread -ldla -lm -Wl,-rpath=/usr/local/Ascend/cann-toolkit/lib64
# 运行
./resnet50_infer
# 输出:
# Top-5 predictions:
# 281: 0.892
# 387: 0.065
# 156: 0.032
# ...
示例3:DVPP 视频预处理
这个示例演示如何用 DVPP 硬件解码视频流。
// src/dvpp_sample/video_decode.c
#include "dvpp/dvpp.h"
int main(int argc, char *argv[]) {
// 1. 初始化
aclliteInit(ACLLITE_BACKEND_DEFAULT);
dvppInit();
// 2. 创建视频解码器(DVPP 硬件解码)
dvppVideoDecoder *decoder = dvppCreateVideoDecoder(
VIDEO_DEC_JPEG, // 编码格式(JPEG、H264、HEVC)
OUTPUT_YUV420SP # 输出格式(YUV420SP 最省带宽)
);
// 3. 打开视频文件
FILE *fp = fopen("test_video.mp4", "rb");
// 4. 解码每一帧
aclliteTensor *frame;
int frame_count = 0;
while (1) {
// 解码一帧
frame = dvppDecodeFrame(decoder, fp);
if (frame == NULL) {
break; // 解码结束
}
// 处理这一帧(比如送进推理模型)
// process_frame(frame);
frame_count++;
if (frame_count % 30 == 0) {
printf("Decoded %d frames\n", frame_count);
}
}
printf("Total frames: %d\n", frame_count);
// 5. 释放资源
dvppDestroyVideoDecoder(decoder);
fclose(fp);
dvppFinalize();
aclliteFinalize();
return 0;
}
DVPP 的优势:
- 硬件解码,比 CPU 快 10 倍
- 输出 YUV420SP 格式,直通 NPU
- 支持批量解码
编译和运行环境配置
编译脚本的使用
# cann-samples 提供了统一的编译脚本
cd cann-samples
# 查看可用的示例目标
make list
# 输出:
# add_sample
# resnet50_infer
# video_decode
# dist_train_sample
# ...
# 编译单个示例
make add_sample
# 编译所有示例
make all
# 清理编译产物
make clean
运行环境配置
# 必须配置的环境变量
export ASCEND_HOME=/usr/local/Ascend/cann-toolkit
export PATH=$ASCEND_HOME/bin:$PATH
export LD_LIBRARY_PATH=$ASCEND_HOME/lib64:$LD_LIBRARY_PATH
export ASCEND_OPP_PATH=$ASCEND_HOME/opp/built-in/acs
# 可选:配置日志级别(方便调试)
export ASCEND_SLOG_PRINT_LEVEL=INFO # 日志级别:DEBUG/INFO/WARNING/ERROR
常见错误排查
错误1:驱动未安装
# 错误现象
npu-smi info
# 输出:npu-smi: command not found
# 解决
sudo ./AscendDriver-*-linux-x86_64.run --full
错误2:AscendCL 初始化失败
# 错误现象
ACLLite error: 507001
# 原因:驱动版本和 Toolkit 版本不匹配
# 解决
# 检查版本一致性
npu-smi info | grep Version
ascendc --version
# 确保大版本一致
错误3:模型转换失败
# 错误现象
ATC error: E80001 model parsing failed
# 原因1:ONNX 模型有不受支持的算子
# 解决:用 --insert_op_conf 指定算子映射
# 原因2:输入 shape 不匹配
# 解决:用 -- input_shape 修正
总结
cann-samples 的上手路径:
- 先跑通 add_sample(最简单的 AscendCL 单算子调用)
- 再看 resnet50_infer(模型加载 + 推理流程)
- 然后 video_decode(DVPP 硬��解码)
- 最后 dist_train_sample(分布式训练)
核心编程模型:
- 初始化 AscendCL(
aclliteInit) - 准备数据(创建 Tensor,拷贝到 Device)
- 执行算子或推理
- 同步等待
- 读取结果
- 释放资源(
aclliteFinalize)
学昇腾 NPU 编程,先跑通 cann-samples 里的基础示例,再去看具体的算子仓库。
附录:常见 CANN 算子仓库对应场景
| 仓库 | 场景 | 示例 |
|---|---|---|
| ops-math | 数学运算 | MatMul, Cast, Softmax |
| ops-blas | 矩阵运算 | GEMM, GEMV |
| ops-transformer | Transformer | Attention, LayerNorm |
| ops-random | 随机数 | RNG, Dropout |
| dvpp | 视频处理 | Decode, Encode, Resize |
学会了 cann-sales,再看这些具体仓库就轻松了。
附录:cann-samples 的快速验证脚本
# 一键验证 cann-samples 是否正常运行
cd cann-samples
# 1. 验证 AscendCL
./scripts/check_acllite.sh
# 输出:ACLLite OK 表示正常
# 2. 验证 DVPP(需要摄像头)
./scripts/check_dvpp.sh
# 输出:DVPP OK 表示正常
# 3. 验证推理
./scripts/check_inference.sh resnet50.om test_image.jpg
# 输出:Top-1: 281 (正确率 > 90% 表示正常)
常见问题:
- 如果 check_acllite.sh 失败,一般是驱动没装好
- 如果 check_dvpp.sh 失败,可能是权限问题(需要 sudo)
- 如果 check_inference.sh 输出错误,先检查输入文件是否存在
仓库地址:https://atomgit.com/cann/cann-samples
更多推荐



所有评论(0)