
去年底接受一个昇腾算子开发的需求,clone完ops-nn、ops-math、ops-cv一堆仓库后,编译报了一屏幕的红色错误。盯着依赖树看了半小时,发现所有仓库都依赖一个叫opbase的东西。
opbase在昇腾CANN的算子生态里扮演的角色,类似Linux系统里的glibc。它是所有算子仓库共用的基础组件和通用库,提供数据类型定义、工具宏、编译器适配、调试基础设施这些"脏活累活"。
它到底提供了什么
opbase的代码结构并不复杂,但覆盖面广。核心分为几个模块:
数据类型层 。昇腾NPU有自己的一套数据类型定义(aclDataType、aclFormat这些),opbase把这些统一封装成跨平台的数据类型头文件。你在ops-math里看到的ops::DataType,底层就是从opbase引的。这样设计的好处是,哪天昇腾CANN升级了数据类型定义,只需要改opbase一处,所有上层算子库自动继承。
编译器适配层 。Ascend C编程要用到毕昇编译器(BiSheng),但开发者的本地环境可能是GCC、Clang各种组合。opbase提供了一套编译宏和适配层,屏蔽编译器差异。比如OPS_HOST_DEVICE这个宏,在NPU侧展开为__device__ __host__,在CPU侧展开为普通函数声明。
调试和日志 。算子开发最头疼的是NPU上不好调试。opbase提供了一套日志宏(OPS_LOGD/OPS_LOGE),编译时可以选择开启哪些日志级别。还集成了算子校验工具(opcheck)的接口层,后面单独讲ops-*仓库时会提到。
性能分析桩点。昇腾CANN的性能分析工具(MsProf)需要算子在关键路径上埋桩。opbase定义了统一的性能分析宏,上层算子直接调用就行,不用关心底层是怎么跟MsProf交互的。
在五层架构里的位置
按照昇腾CANN的五层架构划分,opbase属于第2层(昇腾计算服务层)的基础设施部分,但它在物理上被所有层依赖:
- 第1层(AscendCL)的算子开发接口(Ascend C)依赖opbase的数据类型和编译器适配
- 第2层(AOL算子库)的ops-*系列仓库直接依赖opbase
- 第3层(编译层)的图编译器在生成算子调用代码时,会引用opbase定义的数据类型
- 第4层(执行层)的运行时在加载算子so时,需要跟opbase的ABI兼容
这种跨层依赖在软件架构里其实有点"反模式",但昇腾CANN选择这么做有现实考量:算子开发门槛已经够高了,不能再让开发者处理跨层接口不一致的问题。opbase就是那个"兜底"的统一层。
跟其他仓库的关系
画个简化的依赖图:
opbase
↑
|--- ops-math(数学算子)
|--- ops-nn(神经网络算子)
|--- ops-blas(线性代数算子)
|--- ops-cv(视觉算子)
|--- ops-fft(FFT算子)
|--- ops-rand(随机数算子)
|--- ops-tensor(张量操作算子)
|--- ops-transformer(Transformer算子)
所有ops-*仓库在CMakeLists.txt里都能看到find_package(opbase REQUIRED)。这种设计让每个算子仓库可以专注把自己的算子写对、写快,公共的"基建"交给opbase。
有意思的是catlass(昇腾的算子模板库,类似NVIDIA的CUTLASS)不直接依赖opbase,它通过ops-blas间接依赖。原因是catlass更偏向模板和抽象,opbase偏运行时和数据类型,两者关注点不同。
实际开发中的体验
上个月帮一个做视觉算法的朋友看他的自定义算子为啥编译不过,报错是aclDataType未定义。检查了一圈发现他的CMakeLists.txt里只dependency了ops-cv,没显式依赖opbase。按理说ops-cv应该传递依赖opbase,但他用的ops-cv版本比较老(CANN 8.0之前的),当时依赖关系没处理好。
升级到最新版的ops-cv之后问题自动消失了,因为新版本已经把opbase的传递依赖修好了。这个小坑其实反映了昇腾CANN开源后的一个变化:之前闭源时期的版本依赖比较混乱,开源后在社区推动下依赖关系在逐步规范化。
另一个实际体验是opbase版本兼容性。昇腾CANN的版本迭代比较快(8.0、8.5、即将到来的9.0),opbase的API偶尔会有破坏性变更。如果你的代码依赖了opbase的某个头文件里的内部接口(那些不带OPS_API宏导出的),升级CANN版本时可能会编译失败。官方推荐的做法是只使用opbase公开导出的接口,但文档里没明确标注哪些是公开的、哪些是内部的,只能看头文件里的宏定义。
为什么它重要但没人讲
昇腾CANN的文档和社区内容里,讲FlashAttention、讲MoE、讲算子融合的文章一大堆,但没人讲opbase。原因也很简单:它太底层了,底层到大部分算子开发者不会直接跟它打交道(编译系统自动拉取依赖),底层到写文章讲它很难"出彩"。
但它决定了整个算子生态的稳定性和一致性。类比一下:如果昇腾CANN是一栋楼,ops-transformer、ops-nn这些是楼里的房间,opbase就是地基。你看不到它,但它坏了整栋楼都会塌。
昇腾CANN开源后在AtomGit上能看到opbase的提交记录和Issue,感兴趣的可以去看看社区最近在修什么bug、加什么新特性。仓库地址是atomgit.com/cann/opbase,许可证是Apache-2.0。
写算子也好、用算子也好,遇到编译链接问题的时候,不妨先看看是不是opbase这一层出了岔子。