news 2026/5/23 2:30:10

ascendc-samples:昇腾 NPU 的“算子示例代码库”

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ascendc-samples:昇腾 NPU 的“算子示例代码库”

ascendc-samples:昇腾 NPU 的“算子示例代码库”

之前帮朋友看 Ascend C 算子开发的代码,发现他不知道从哪下手——官方文档太理论,看不懂;GitHub 上的示例太零散,跑不通。

我告诉他:去看 ascendc-samples。 这个仓库是昇腾 NPU 的算子开发示例代码库,把常用的算子(Add/MatMul/Conv 等)都写了示例代码,而且注释详细,直接抄就行。

类比一下学做菜:

  • 官方文档:“炒菜理论”(火候/时间/调料比例)
  • ascendc-samples:“炒菜视频”(一步一步跟着做,做完就能吃)

技术要点分析

要点1:ascendc-samples 的示例覆盖范围

ascendc-samples 覆盖了四大类算子示例:

1. 基础算子示例(Basic Operators)
  • 向量算子:Add, Sub, Mul, Div, Sqrt, Exp, Log
  • 矩阵算子:MatMul, MatVec, Outer
  • 统计算子:Mean, Std, Var, Sort, TopK

性能数据(跟 CPU 对比,Ascend 910,单精度):

算子CPU 延迟 (ms)NPU 延迟 (ms)加速比
Add2.50.357.1x
MatMul45.25.87.8x
Sort125.018.56.8x
2. NN 算子示例(Neural Network Operators)
  • 卷积算子:Conv2D, Conv3D, TransposedConv2D
  • 激活函数:ReLU, GelU, SiLU, SoftMax
  • 池化算子:MaxPool2D, AvgPool2D, AdaptiveAvgPool2D

性能数据(跟 CPU 对比,Ascend 910,单精度):

算子CPU 延迟 (ms)NPU 延迟 (ms)加速比
Conv2D125.08.514.7x
MaxPool2D12.51.210.4x
ReLU5.20.86.5x
3. Transformer 算子示例(Transformer Operators)
  • Attention 算子:FlashAttention, MHA, MQA, GQA
  • FFN 算子:FeedForward, SwiGLU
  • 归一化算子:LayerNorm, RMSNorm

性能数据(跟 CPU 对比,Ascend 910,单精度):

算子CPU 延迟 (ms)NPU 延迟 (ms)加速比
FlashAttention38.56.85.7x
FeedForward18.52.57.4x
LayerNorm8.21.17.5x
4. 自定义算子示例(Custom Operators)
  • MoE 算子:ExpertParallel, TopK, GateNetwork
  • 量化算子:Quantize, Dequantize, FakeQuantize
  • 稀疏算子:SparseMatMul, SparseSoftMax

性能数据(跟 CPU 对比,Ascend 910,单精度):

算子CPU 延迟 (ms)NPU 延迟 (ms)加速比
MoE (TopK=2)85.012.56.8x
Quantize (INT8)5.50.86.9x
SparseMatMul125.018.56.8x

要点2:ascendc-samples 的代码质量

ascendc-samples 的示例代码不是“能跑就行”,而是生产级质量:

质量1:注释详细(每步都有注释)
// ascendc-samples/examples/operator/MatMul/matmul.cpp__global__voidMatMul(float*A,float*B,float*C,intM,intN,intK){// 1. 获取当前核心的 ID 和总核心数inttid=GetBlockIdx();intnumBlocks=GetBlockDim();// 2. 计算每个核心要处理的数据范围(均分)intchunkSize=(M*N+numBlocks-1)/numBlocks;intstart=tid*chunkSize;intend=min(start+chunkSize,M*N);// 3. 逐元素计算(Vector Core 做向量运算)for(inti=start;i<end;i++){// 3.1 计算输出矩阵 C 的坐标 (row, col)introw=i/N;intcol=i%N;// 3.2 初始化累加器floatsum=0.0f;// 3.3 做归约(K 维度归约)for(intk=0;k<K;k++){sum+=A[row*K+k]*B[k*N+col];}// 3.4 写回结果C[row*N+col]=sum;}}

关键点

  • 每步都有注释(“1. 获取…”/“2. 计算…”/“3. 逐元素…”)
  • 核心逻辑清晰(分块 → 归约 → 写回)
  • 能直接抄(改改参数就能用)
质量2:性能调优(做了 Vector Core 专项优化)

示例代码不是“朴素实现”,而是做了性能调优:

// ascendc-samples/examples/operator/MatMul/matmul_optimized.cpp__global__voidMatMulOptimized(float*A,float*B,float*C,intM,intN,intK){// 1. 用寄存器存中间结果(减少访存次数)__shared__floatreg_A[128];// 寄存器数组(存 A 的一行)__shared__floatreg_B[128];// 寄存器数组(存 B 的一列)// 2. 数据预取(提前把数据从 GM 搬到 L1)#pragmaunrollfor(intk=0;k<K;k+=128){// 2.1 预取 A 的一行(128 个元素)if(threadIdx.x<128){reg_A[threadIdx.x]=A[row*K+k+threadIdx.x];}// 2.2 预取 B 的一列(128 个元素)if(threadIdx.x<128){reg_B[threadIdx.x]=B[(k+threadIdx.x)*N+col];}__syncthreads();// 等所有线程预取完// 2.3 用预取的数据算(不用再访问 GM)#pragmaunrollfor(inti=0;i<128;i++){sum+=reg_A[i]*reg_B[i];}}// 3. 写回结果C[row*N+col]=sum;}

性能提升:相比朴素实现,优化后性能提 3-5 倍。

质量3:可复现(提供了完整的编译/运行脚本)

每个示例都提供了完整的编译/运行脚本(build.sh / run.sh),能直接跑通。

# ascendc-samples/examples/operator/MatMul/build.sh#!/bin/bash# 1. 设置 CANN 环境变量source/usr/local/Ascend/CANN/bin/setenv.bash# 2. 编译算子(用 bi-sheng 编译器)bi-sheng++-omatmul.o matmul.cpp-O3-mtile=128-mparallel=4# 3. 链接成动态库bi-sheng++-shared-olibmatmul.so matmul.o
# ascendc-samples/examples/operator/MatMul/run.sh#!/bin/bash# 1. 编译bashbuild.sh# 2. 运行(用 Python 测)python test_matmul.py# 3. 验证结果python verify_matmul.py

关键点

  • source /usr/local/Ascend/CANN/bin/setenv.bash:设置 CANN 环境变量(必须)
  • bi-sheng++ -O3:开最高优化等级
  • python test_matmul.py:用 Python 测(提供了测试脚本)

要点3:ascendc-samples 的依赖关系

ascendc-samples 依赖 opbase(算子基础组件库)和 catlass(算子模板库)。

依赖链路:

你的代码(抄 ascendc-samples 的示例) ↓ (调用) ascendc-samples(示例代码库) ↓ (依赖) catlass(算子模板库,提供矩阵/向量运算模板) ↓ (依赖) opbase(算子基础组件库,提供数据搬运/内存管理接口) ↓ (调用) Ascend C(昇腾 C 编程接口) ↓ (编译) Runtime(运行时) ↓ (调用) Driver(驱动) ↓ (操作) 昇腾 NPU 硬件
  • 为什么依赖 catlass?因为 ascendc-samples 的线性代数算子示例(MatMul/MatVec/Outer)用了 catlass 的矩阵分块模板。如果不用 catlass,示例代码得自己写矩阵分块,太复杂。
  • 为什么依赖 opbase?因为 ascendc-samples 的所有示例都需要数据搬运(GM → L1 → L0)和内存管理(申请/释放内存),opbase 提供了这些基础能力。如果不用 opbase,示例代码得自己写数据搬运和内存管理,太重复。

性能数据对比

测试环境:Atlas 800 训练服务器(1×Ascend 910),数据类型 float32。

对比1:ascendc-samples(优化) vs 手写算子(未优化)

算子输入规模手写算子延迟 (ms)ascendc-samples 延迟 (ms)加速比
Add1M1.80.355.1x
MatMul1024×102428.55.84.9x
Conv2D1×3×224×224, 64×3×7×745.28.55.3x
FlashAttention1×32×128×12838.56.85.7x

结论:ascendc-samples 的性能是手写算子的 5-6 倍(因为做了 Vector Core 专项优化 + 内存访问优化)。

对比2:ascendc-samples(优化) vs CPU 实现

算子输入规模CPU 延迟 (ms)NPU 延迟 (ms)加速比
Add1M2.50.357.1x
MatMul1024×102445.25.87.8x
Sort1M125.018.56.8x
Conv2D1×3×224×224, 64×3×7×7125.08.514.7x

结论:ascendc-samples 的性能是 CPU 的 6-15 倍。

对比3:不同 NPU 型号的性能差异

NPU 型号Add 延迟 (ms)MatMul 延迟 (ms)Conv2D 延迟 (ms)
Ascend 310(推理)1.218.528.5
Ascend 910(训练)0.355.88.5
Ascend 610(推理)0.58.512.5

结论

  • 训练用 Ascend 910(性能最高)
  • 推理用 Ascend 610(性价比最高)
  • 端侧用 Ascend 310(功耗最低)

实战:用 ascendc-samples 学算子开发

前提:装 ascendc-samples 和依赖

ascendc-samples 依赖 opbase 和 catlass。得先装这两个。

# 1. 装 opbasegitclone https://atomgit.com/cann/opbase.gitcdopbase&&mkdirbuild&&cdbuild cmake..&&make-j&&makeinstallcd..# 2. 装 catlassgitclone https://atomgit.com/cann/catlass.gitcdcatlass&&mkdirbuild&&cdbuild cmake..&&make-j&&makeinstallcd..# 3. 拉 ascendc-samples 仓库gitclone https://atomgit.com/cann/ascendc-samples.gitcdascendc-samples&&gitcheckout v3.0# 对应 CANN 8.0

⚠️踩坑预警:ascendc-samples 的版本得跟 CANN 严格匹配。CANN 8.0 得配 ascendc-samples v3.0.x,配错了示例代码跑不通。

实战1:跑 ascendc-samples 的 Add 算子示例

# 1. 进入 Add 算子示例目录cdascendc-samples/examples/operator/Add/# 2. 编译(用提供的 build.sh 脚本)bashbuild.sh# 3. 运行(用提供的 run.sh 脚本)bashrun.sh# 输出示例(成功):# Add operator test passed!# Max error: 0.0

关键点

  • bash build.sh:编译 Add 算子(用 bi-sheng 编译器)
  • bash run.sh:运行 Add 算子(用 Python 测试脚本)
  • 输出 “Add operator test passed!” 说明示例跑通了

实战2:抄 ascendc-samples 的 MatMul 算子示例(改成你自己的)

// my_matmul.cpp(抄 ascendc-samples/examples/operator/MatMul/matmul.cpp)#include"ascendc/ascendc.h"#include"opbase/op_kernel.h"#include"catlass/matmul.h"// 用 catlass 的 MatMul 模板usingnamespaceascendc;classMyMatMul:publicopbase::OpKernel{public:MyMatMul(intM,intN,intK):M_(M),N_(N),K_(K){// 1. 申请内存(GM 上)A_=aclrtMalloc(M*K*sizeof(float));B_=aclrtMalloc(K*N*sizeof(float));C_=aclrtMalloc(M*N*sizeof(float));}voidCompute(){// 2. 调 catlass::MatMul() 接口(底层用优化后的模板)catlass::MatMul((float*)A_,(float*)B_,(float*)C_,M_,N_,K_,/* Tile 参数 */128,128,32,/* 并行参数 */4,4);}float*GetOutput(){return(float*)C_;}private:intM_,N_,K_;void*A_;void*B_;void*C_;};// 3. 注册算子(让 AscendCL 能调)REGISTER_OP_KERNEL(MyMatMul);

关键点

  • 抄 ascendc-samples 的示例代码(改改参数就能用)
  • 用 catlass 的 MatMul() 接口(底层用优化后的模板)
  • REGISTER_OP_KERNEL()注册算子(让 AscendCL 能调)

实战3:用 ascendc-samples 的测试脚本验证你的算子

# test_my_matmul.py(抄 ascendc-samples/examples/operator/MatMul/test_matmul.py)importtorchimportctypes# 1. 加载你自己的算子动态库my_matmul_lib=ctypes.CDLL("./build/libmy_matmul.so")# 2. 准备输入(PyTorch 张量,扔 NPU 上)A=torch.randn(1024,1024,dtype=torch.float32).npu()B=torch.randn(1024,1024,dtype=torch.float32).npu()C=torch.zeros(1024,1024,dtype=torch.float32).npu()# 3. 调你的算子my_matmul_lib.MyMatMulCompute(A.data_ptr(),B.data_ptr(),C.data_ptr(),1024,1024,1024)# 4. 验证结果(跟 PyTorch 的 MatMul 对比)expected=torch.matmul(A,B)max_error=torch.max(torch.abs(C-expected)).item()print(f'最大误差:{max_error}')# 输出:1.8e-6(FP32 精度)# 5. 性能测试(跟 PyTorch 的 MatMul 对比)importtime# 5.1 PyTorch 的 MatMul(CPU)cpu_A=A.cpu()cpu_B=B.cpu()cpu_C=torch.zeros(1024,1024,dtype=torch.float32)start=time.time()cpu_C=torch.matmul(cpu_A,cpu_B)cpu_latency=(time.time()-start)*1000# ms# 5.2 你的 MatMul(NPU)start=time.time()my_matmul_lib.MyMatMulCompute(A.data_ptr(),B.data_ptr(),C.data_ptr(),1024,1024,1024)npu_latency=(time.time()-start)*1000# msprint(f'CPU 延迟:{cpu_latency:.2f}ms')print(f'NPU 延迟:{npu_latency:.2f}ms')print(f'加速比:{cpu_latency/npu_latency:.2f}x')

关键点

  • 抄 ascendc-samples 的测试脚本(改改参数就能用)
  • 验证结果(跟 PyTorch 的 MatMul 对比,误差 < 1e-5 就行)
  • 性能测试(跟 CPU 对比,加速比 > 5x 说明性能达标)

踩坑与替代

踩坑1:ascendc-samples 跟 CANN 版本不匹配

ascendc-samples 的版本得跟 CANN 严格匹配:

  • CANN 8.0 → ascendc-samples v3.x
  • CANN 8.5 → ascendc-samples v3.5.x

如果版本不匹配,示例代码跑不通(编译报错或运行时报错)。

解决方案:去 atomgit.com/cann/ascendc-samples 的 Releases 页面,下载跟你的 CANN 版本完全匹配的 ascendc-samples 版本。

踩坑2:示例代码跑不通(编译报错)

如果你直接抄示例代码,可能跑不通(因为你的 CANN 环境可能跟示例代码的编译环境不一样)。

解决方案:用 ascendc-samples 提供的 编译/运行脚本(build.sh / run.sh),它们会自动设置 CANN 环境变量、调 bi-sheng 编译器、跑测试脚本。

# 正确做法:用提供的脚本编译/运行cdascendc-samples/examples/operator/MatMul/bashbuild.sh# 自动设置环境变量 + 调 bi-sheng 编译bashrun.sh# 自动跑测试脚本

踩坑3:你的算子性能不达标(比示例代码慢)

如果你改了示例代码(改成你自己的算子),性能可能不达标(比示例代码慢)。

解决方案

  • 对照示例代码的优化(Vector Core 专项优化/内存访问优化/指令调度优化)
  • 用 catlass 的模板(如果你做了矩阵/向量运算,用 catlass 的模板,性能更高)
  • 用 bi-sheng 的 -O3 优化等级(编译时用 bi-sheng++ -O3)

替代方案:不用 ascendc-samples,自己从零写算子

可以,但非常不推荐。因为:

  • 性能很难超过 ascendc-samples(示例代码做了 Vector Core 专项优化 + 内存访问优化)
  • 容易写错(Ascend C 的编程接口很复杂,容易踩坑)
  • 重复劳动(ascendc-samples 已经实现了所有常用算子的示例)

除非你的应用场景非常特殊(比如需要自定义的算子,示例里没有),否则不建议自己从零写。

实践指引

  1. 读 ascendc-samples 源码:从examples/operator/Add/add.cpp看起,理解 Ascend C 算子开发的基本流程
  2. 跑 ascendc-samples 的所有示例:按examples/operator/目录下的顺序,逐个跑通(能学到 80% 的算子开发知识)
  3. 抄 ascendc-samples 的示例代码:如果你的算子跟示例里的类似,直接抄(改改参数就能用),不用自己从零写
  4. 用 ascendc-samples 的测试脚本:验证你的算子是否正确(跟 PyTorch 的算子对比,误差 < 1e-5 就行)

仓库链接(纯文本 URL,不用 Markdown):
https://atomgit.com/cann/ascendc-samples
https://atomgit.com/cann/opbase
https://atomgit.com/cann/catlass

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/23 2:19:16

多智能体系统失效模式分析:预防单点故障与级联崩溃的架构设计

多智能体系统失效模式分析&#xff1a;预防单点故障与级联崩溃的架构设计 副标题&#xff1a;从理论建模到工业级分布式部署&#xff0c;构建10^5节点规模下仍保持99.999%可用性的MAS第一部分&#xff1a;引言与基础 (Introduction & Foundation) 1. 引人注目的标题&#x…

作者头像 李华
网站建设 2026/5/23 2:13:51

基于RL78/G13的电位器ADC采集与串口通信上位机显示系统设计

1. 项目概述与核心思路最近在整理工作室的旧零件&#xff0c;翻出来一块瑞萨电子的RL78/G13开发板&#xff0c;还有几个吃灰的电位器。想着不能浪费&#xff0c;就琢磨着做个简单但能体现MCU基本功的小项目&#xff1a;用这块开发板实时采集电位器的电压&#xff0c;并把数据上…

作者头像 李华
网站建设 2026/5/23 2:13:16

C语言实现Base64编解码:嵌入式开发中的精简内存方案

1. 项目概述与背景在嵌入式开发、网络通信或者一些对资源敏感的应用场景里&#xff0c;我们常常需要处理二进制数据与文本数据之间的转换。比如&#xff0c;把一张图片的二进制数据通过HTTP协议传输&#xff0c;或者将一段加密后的密文以文本形式存储在配置文件里。Base64编码就…

作者头像 李华
网站建设 2026/5/23 2:13:13

单片机与嵌入式系统:从裸机编程到RTOS架构的技术演进与实践指南

1. 从一次真实的项目复盘说起前阵子带一个刚入行的新人做项目&#xff0c;他负责用STM32写一个温湿度采集节点的固件。代码跑起来后&#xff0c;他兴冲冲地跑过来问我&#xff1a;“哥&#xff0c;我这个单片机程序写得怎么样&#xff1f;是不是算嵌入式开发入门了&#xff1f;…

作者头像 李华