前言

刚接触昇腾NPU,对着Ascend C编程指南看了3天,还是写不出一个能跑的MatMul。不是指南不好,是缺"能直接跑的样例代码"——看10页理论,不如跑通一个样例。

cann-samples是CANN社区的样本代码仓库,提供从"Hello NPU"到"Transformer推理全流程"的完整样例。装好CANN,跑通样例,再去写自己的算子,效率涨3倍。

cann-samples 的定位

cann-samples是CANN五层架构中的示例与学习资源仓库,提供各类算子、应用的完整可运行代码。

CANN 示例与学习资源(7个):
├─ cann-learning-hub(社区学习中心)
├─ cann-samples ← 你在这(样本代码仓库)
├─ cann-recipes-infer(推理菜谱)
├─ cann-recipes-train(训练菜谱)
├─ cann-recipes-embodied-intelligence(具身智能菜谱)
├─ cann-recipes-spatial-intelligence(空间智能菜谱)
└─ cann-recipes-harmony-infer(Harmony推理菜谱)

核心内容清单:

类别 样例数量 适用场景
Ascend C 算子开发 23个 算子开发入门
PyTorch 模型迁移 15个 模型适配
MindSpore 模型开发 12个 MindSpore开发
推理部署样例 18个 推理落地
性能调优样例 9个 性能优化

工程经验: cann-samples的样例都是"能直接跑"的(有完整编译脚本、运行脚本、预期输出)。不复用样例自己从零写,调试时间多2-3倍。试过自己写Ascend C MatMul,调试3天;基于cann-samples的matmul样例改,半天搞定。

快速上手:5分钟跑通第一个样例

1. 环境准备
# 确认CANN已安装
ls /usr/local/Ascend/
# 输出:ascend-cli-23.0.0.linux-x86_64.run  nnrt-7.0.0.py3-none-linux_x86_64.sh

# 确认NPU可用
npu-smi info
# 输出:NPU ID、芯片型号、显存大小

# 克隆cann-samples仓库
git clone https://atomgit.com/cann/cann-samples.git
cd cann-samples
2. 跑通"Hello NPU"(Ascend C版)
// samples/ascend_c/hello_npu/hello_npu.cpp
#include "kernel_operator.h"

__aicore__ void hello_npu(uint8_t* output) {
    // 每个AI Core写一个"HELLO"字符
    auto gid = GetBlockIdx();  // 当前AI Core编号
    if (gid < 5) {
        output[gid] = "HELLO"[gid];
    }
}

// 算子入口
extern "C" __global__ __aicore__ void hello_npu_kernel(uint8_t* output) {
    hello_npu(output);
}

// 编译
# npu-smi set -t hello_npu -s 0 -d hello_npu.o hello_npu.cpp

# 运行
# python run_hello_npu.py
# 输出:HELLO
# 编译和运行
cd samples/ascend_c/hello_npu/
bash build.sh
bash run.sh

# 输出:
# [INFO] NPU input: [0, 0, 0, 0, 0]
# [INFO] NPU output: [72, 69, 76, 76, 79]  ← ASCII("HELLO")
# [INFO] Run success!

工程经验: 第一个样例跑不通,99%是环境没配好(CANN路径没加到LD_LIBRARY_PATH)。bash build.sh失败时,看build.log最后10行,通常能找到原因(缺库、路径错、权限不够)。

3. 跑通MatMul算子样例
cd samples/ascend_c/matmul/

# 看样例结构
tree .
# 输出:
# .
# ├── matmul.cpp          # 算子实现
# ├── main.cpp           # 主机端调用代码
# ├── build.sh           # 编译脚本
# ├── run.sh             # 运行脚本
# └── test_data/         # 测试数据
#     ├── A.bin          # 输入矩阵A
#     ├── B.bin          # 输入矩阵B
#     └── C_gt.bin      # 标准答案(Ground Truth)

# 编译
bash build.sh

# 运行
bash run.sh

# 输出:
# [INFO] MatMul M=64, K=64, N=64
# [INFO] NPU result vs Ground Truth: MAX ERROR=0.001 (FP16误差)
# [INFO] Run success!

样例代码核心逻辑:

// samples/ascend_c/matmul/matmul.cpp(精简版)
#include "kernel_operator.h"

constexpr int TILE_M = 64;
constexpr int TILE_K = 64;
constexpr int TILE_N = 64;

class MatMulKernel {
public:
    __aicore__ void Process(GM_ADDR a, GM_ADDR b, GM_ADDR c,
                           int M, int K, int N) {
        TPipe pipe;
        TBuf<TPosition::A1> A_L0A;
        TBuf<TPosition::B1> B_L0B;
        TBuf<TPosition::C1> C_L0C;
        
        pipe.AllocBuf(A_L0A, TILE_M * TILE_K * sizeof(half));
        pipe.AllocBuf(B_L0B, TILE_K * TILE_N * sizeof(half));
        pipe.AllocBuf(C_L0C, TILE_M * TILE_N * sizeof(half));
        
        for (int m = 0; m < M; m += TILE_M) {
            for (int n = 0; n < N; n += TILE_N) {
                // 初始化C_tile为0
                DataCopy(C_L0C, 0, TILE_M * TILE_N * sizeof(half));
                
                for (int k = 0; k < K; k += TILE_K) {
                    // 从HBM读A_tile到L0A
                    DataCopy(A_L0A, a + m * K + k, TILE_M * TILE_K * sizeof(half));
                    
                    // 从HBM读B_tile到L0B
                    DataCopy(B_L0B, b + k * N + n, TILE_K * TILE_N * sizeof(half));
                    
                    // Cube算A_tile × B_tile,累加到C_tile
                    MatMul(C_L0C, A_L0A, B_L0B, TILE_M, TILE_K, TILE_N,
                           { .accumulate = true });
                }
                
                // 把C_tile写回HBM
                DataCopy(c + m * N + n, C_L0C, TILE_M * TILE_N * sizeof(half));
            }
        }
    }
};

工程经验: cann-samples的MatMul样例是"标准实现"(无预取、无流水线),性能只有ops-math的60%。但它结构简单,适合学习Ascend C编程模型。要性能,看cann-recipes-infer的优化版。

PyTorch 模型迁移样例

cann-samples提供15个PyTorch模型迁移样例,覆盖CV、NLP、推荐系统。

样例1:ResNet50 迁移到昇腾
# samples/pytorch/resnet50/train.py(精简版)
import torch
import torch_npu  # 导入NPU后端

# 1. 模型放到NPU
model = resnet50(pretrained=True).npu()

# 2. 数据放到NPU
inputs = inputs.npu()
labels = labels.npu()

# 3. 优化器用NPU版本
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)

# 4. 训练(跟GPU代码一模一样)
for epoch in range(10):
    for batch in dataloader:
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
cd samples/pytorch/resnet50/
bash run_train.sh

# 输出:
# [INFO] Epoch 1/10, Loss: 1.234
# [INFO] Epoch 2/10, Loss: 0.987
# ...
# [INFO] Train success! Throughput: 1234 images/s

工程经验: PyTorch模型迁移到昇腾,90%的情况只要.npu()就够了。剩下10%是算子不支持(报错ACL_ERROR_NONE: Op Xxx not supported),要解决。

样例2:LLaMA2-7B 推理部署
# samples/pytorch/llama2-7b/infer.py(精简版)
import torch
import torch_npu
from transformers import LLaMAForCausalLM, LLaMATokenizer

# 1. 加载模型(自动映射到NPU)
model = LLaMAForCausalLM.from_pretrained("llama2-7b").npu()

# 2. 编译模型(开GE图编译)
model = torch.compile(model, backend="npu")

# 3. 推理
inputs = tokenizer("Hello, ", return_tensors="pt").input_ids.npu()
outputs = model.generate(inputs, max_new_tokens=50)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
cd samples/pytorch/llama2-7b/
bash run_infer.sh

# 输出:
# [INFO] Load model: llama2-7b, params: 6.7B
# [INFO] Compile model: GE graph compile time: 12.3s
# [INFO] Generate: "Hello, how are you doing today? I hope..."
# [INFO] Throughput: 34 tokens/s

性能调优样例

cann-samples提供9个性能调优样例,教你怎么用msprof分析性能瓶颈。

样例:MatMul 性能调优
cd samples/profiling/matmul/

# 1. 跑msprof做性能分析
msprof --output=./prof --aic-metrics=memory_bandwidth_utilization \
       python run_matmul.py

# 2. 导出报告
msprof --export=on --output=./prof

# 3. 打开报告(prof/summary/report.html)
# 看"算子耗时分布":MatMul占85% → 计算瓶颈
# 看"HBM带宽利用率":35% → 访存瓶颈

# 4. 优化(开Tiling、开预取)
# 修改matmul.cpp,加Tiling、加预取
bash rebuild.sh

# 5. 再跑msprof
msprof --output=./prof2 --aic-metrics=memory_bandwidth_utilization \
       python run_matmul.py

# 6. 对比报告
# HBM带宽利用率:35% → 82%(+134%)
# 吞吐:34 tokens/s → 89 tokens/s(+162%)

工程经验: 性能调优的正确流程:先profile找瓶颈 → 针对性优化 → 再profile验证。不复用profile盲目优化,可能优化半天瓶颈不在这。

踩坑实录

坑1:样例编译失败(CANN路径找不到)

bash build.sh报错fatal error: kernel_operator.h: No such file or directory

原因:CANN没装,或者ASCEND_HOME环境变量没设。

解决:

# 设ASCEND_HOME
export ASCEND_HOME=/usr/local/Ascend/
export LD_LIBRARY_PATH=$ASCEND_HOME/lib64:$LD_LIBRARY_PATH
export PATH=$ASCEND_HOME/bin:$PATH

# 再编译
bash build.sh

坑2:样例运行失败(NPU驱动没装)

bash run.sh报错ACL_ERROR_NONE: Initialize failed, reason: driver not installed

原因:NPU驱动没装,或者驱动版本跟CANN版本不匹配。

解决:

# 查驱动版本
npu-smi info | grep "Driver Version"

# 查CANN要求的驱动版本
cat $ASCEND_HOME/version.info

# 版本不匹配,重装驱动
sudo bash ASCEND/ascend-driver_23.0.0_linux-x86_64.run --full

坑3:PyTorch样例跑不通(torch_npu没装)

import torch_npu报错ModuleNotFoundError: No module named 'torch_npu'

原因:torch_npu没装。

解决:

# 装torch_npu(匹配PyTorch版本)
pip install torch_npu==2.1.0  # PyTorch 2.1.0对应torch_npu 2.1.0

# 验证
python -c "import torch_npu; print(torch_npu.__version__)"

坑4:样例输出跟文档不一致(CANN版本不同)

样例输出文档写"Throughput: 1234 images/s",实际跑出来"Throughput: 987 images/s"。

原因:CANN版本不同,性能有差异(新版通常更优)。

解决:看样例目录下的README.md,确认CANN版本要求。装对应版本的CANN。

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

https://atomgit.com/cann/cann-learning-hub

https://atomgit.com/cann/cann-recipes-infer

Logo

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

更多推荐