AMCT amct_ops:昇腾 NPU 自定义算子包 amct_ops 的构建、接口与扩展指南
【免费下载链接】amctAMCT是CANN提供的昇腾AI处理器亲和的模型压缩工具仓。项目地址: https://gitcode.com/cann/amct
amct_ops是 AMCT 仓库中面向昇腾 NPU 的自定义算子层,承载 PyTorch /torch_npu尚未覆盖的低比特数据类型转换、量化辅助算子等硬件级实现。本文围绕 amct_ops/README_en.md 展开,说明它在 AMCT 中与amct_pytorch/的分工、支持的算子接口、wheel 构建流程、依赖约束、使用方式,以及新增算子时应遵循的目录结构和命名空间约束。
读完本文,你可以:
- 理解
amct_ops作为独立 PyTorch 扩展包的定位:它提供可独立安装、可复用的 NPU 算子.so与 Python 接口; - 掌握
ops_build.sh的构建参数、平台选择、产物路径与安装方式; - 理解
torch.ops.amct.<op>与from amct_ops.<pkg> import <op>两类接口的底层注册关系; - 了解新增算子时的目录布局、自动打包机制与
amct命名空间约束。
1. amct_ops 在 AMCT 中的定位
amct_ops/README_en.md 将amct_ops定义为 AMCT 的 NPU 自定义算子层。它的核心职责是实现 PyTorch /torch_npu尚未覆盖的硬件级算子,例如低比特量化、数据类型转换等。与amct_pytorch/相比,amct_ops更贴近底层硬件实现,而不是压缩算法和流程编排层。
从仓库结构看,amct_pytorch/主要承载 Python 算法、压缩流程、校准、量化模块、部署与评估等上层能力;而amct_ops/则按算子拆分子目录,每个算子通常包含 Ascend C kernel、C++ 扩展绑定、Python 接口和独立 CMake 编译入口。这种分层让上层算法可以通过 Python 接口或torch.ops.amct调用算子,而无需直接关注 Ascend C kernel、C++ extension 和 CMake 编译细节。
2. amct_pytorch 与 amct_ops 的分工
amct_ops/README_en.md 给出的分工如下:
| 维度 | amct_pytorch/ | amct_ops/ |
|---|---|---|
| 关注点 | 压缩算法与流程编排 | 算子底层实现 |
| 语言 | Python | Ascend C kernel、C++ binding、Python 接口 |
| 产物 | .tar.gz源码压缩包 | wheel 包,包含.so与 Python 接口 |
| 复用性 | 绑定 AMCT 流程 | 独立 PyTorch 扩展,不强依赖 AMCT 主流程 |
这组分工体现了“算法层与算子层解耦”的设计:amct_pytorch/负责把模型压缩、量化、校准、评估等流程组织起来;amct_ops/负责提供真正落在 NPU 上执行的底层算子。对使用者来说,这种解耦有两个直接好处。
第一,Python 算法代码不需要暴露底层实现细节。上层模块可以通过稳定的 Python 函数或torch.ops.amct命名空间调用算子,不必知道 Ascend C tiling、kernel 编译或 CMake 配置。
第二,算子可以按模块独立开发、构建和测试。新增低比特类型、量化辅助算子或调整 NPU 实现时,只要遵循算子目录约定,就可以被统一构建脚本自动发现并打包,而不会干扰主算法目录。
3. 支持的算子与 Python 接口
amct_ops/README_en.md 当前列出的核心算子如下:
| 算子 | 说明 | Python 接口 |
|---|---|---|
hifloat8_cast | FP16 / BF16 与 HiFloat8 双向转换 | encode_to_hifloat8(x)、decode_from_hifloat8(x, dtype) |
hifloat4_cast | FP16 / BF16 到 HiFloat4 的 fake-quant 仿真,使用 64 元素块缩放 | hifloat4_fake_quant(x, qdim=-1) |
3.1 hifloat8_cast:FP16/BF16 与 HiFloat8 双向转换
hifloat8_cast提供两组接口:
from amct_ops.hifloat8_cast import encode_to_hifloat8, decode_from_hifloat8 y = encode_to_hifloat8(x) # FP16/BF16 → uint8 z = decode_from_hifloat8(y) # uint8 → bfloat16(默认) z = decode_from_hifloat8(y, torch.float16) # uint8 → float16从 amct_ops/hifloat8_cast/python/hifloat8_cast/ops.py 可以看到,这两个 Python 函数最终都转发到torch.ops.amct.encode_to_hifloat8和torch.ops.amct.decode_from_hifloat8。因此模块导入方式和torch.ops方式只是调用入口不同,底层注册的是同一组算子。
在 C++ 侧,amct_ops/hifloat8_cast/op_extension/register.cpp 中定义了算子 schema:
TORCH_LIBRARY_FRAGMENT(amct, m) { m.def("encode_to_hifloat8(Tensor input) -> Tensor"); m.def("decode_from_hifloat8(Tensor input, ScalarType? dtype=None) -> Tensor"); }并注册了PrivateUse1后端实现与Meta后端实现:
TORCH_LIBRARY_IMPL(amct, PrivateUse1, m) { m.impl("encode_to_hifloat8", TORCH_FN(EncodeImpl)); m.impl("decode_from_hifloat8", TORCH_FN(DecodeImpl)); } TORCH_LIBRARY_IMPL(amct, Meta, m) { m.impl("encode_to_hifloat8", &EncodeMeta); m.impl("decode_from_hifloat8", &DecodeMeta); }其中PrivateUse1是torch_npu使用的后端命名空间,Meta实现只根据输入 shape 返回对应 dtype 的空张量,用于 shape 推导和符号执行场景。
具体校验逻辑也可以从源码中确认:encode_to_hifloat8要求输入为float16或bfloat16;decode_from_hifloat8要求输入为uint8,输出 dtype 只能是float16或bfloat16,默认输出bfloat16。
3.2 hifloat4_cast:HiFloat4 fake-quant 仿真
hifloat4_cast提供 HiFloat4 fake-quant 仿真接口:
from amct_ops.hifloat4_cast import hifloat4_fake_quant y = hifloat4_fake_quant(x) # FP16/BF16 → HiF4 → FP16/BF16 y = hifloat4_fake_quant(x, qdim=-1) # 指定量化维度amct_ops/hifloat4_cast/python/hifloat4_cast/init.py 的文档说明该算子注册为torch.ops.amct.hifloat4_fake_quant,并暴露为模块级函数hifloat4_fake_quant(x, qdim=-1)。从接口语义看,它模拟的是 FP16/BF16 输入经过 HiFloat4 量化路径后再回到 FP16/BF16 的过程,适合用于低比特格式精度评估、算法仿真或部署前行为验证。
4. 目录结构与构建入口
amct_ops/README_en.md 给出的目录结构如下:
amct_ops/ ├── hifloat8_cast/ # HiFloat8 conversion operator source code (kernel + binding + Python interface) ├── hifloat4_cast/ # HiFloat4 FP→HiF4→FP simulation operator source code ├── svd_quant/ # SVD quantization operator with mixed Mxfp4/Bf16 ├── ops_build.sh # Unified build entry ├── setup.py # wheel packaging configuration └── ops_init.py # Copied as __init__.py during packaging, providing package interface and documentation构建过程中会生成build/、dist/、staging/、<op>/build/等目录。README 说明这些是本地构建产物,不需要提交。
从 amct_ops/ops_build.sh 的实现看,构建流程分为四个阶段:
- 检测主机 CPU 架构,并将
x86_64/amd64映射为 CANN 的x86_64-linux,将aarch64/arm64映射为aarch64-linux; - 加载 CANN 环境,要求环境变量
ASCEND_HOME_PATH已设置,并且$ASCEND_HOME_PATH/set_env.sh存在; - 编译算子。默认扫描
amct_ops/下包含CMakeLists.txt的子目录,逐个执行 CMake 配置和编译; - 将各算子的 Python 包和
.so汇集到staging/amct_ops/,最后通过pip wheel . -w dist/ --no-deps --no-build-isolation -q生成 wheel。
5. 依赖要求
amct_ops/README_en.md 给出的依赖要求如下:
| 依赖 | 版本 |
|---|---|
| Python | >=3.9 |
| PyTorch | 2.7.1或2.1.0(需配套torch_npu) |
| GCC / CMake | GCC>= 7.3,CMake>= 3.16,推荐 CMake 3.20 |
| CANN(Toolkit & Ops) | >= 9.0.0(需提前安装 NPU 驱动 / 固件) |
完整环境部署可参考 docs/zh/quick_install.md。
从 amct_ops/hifloat8_cast/CMakeLists.txt 看,算子编译还会检查 CANN 目录结构、Torch 路径和torch_npu路径,并链接torch_npu、tiling_api、register、platform、unified_dlog、graph_base、ascendcl、ascendc_runtime等库。这说明amct_ops不是普通 Python wheel,而是依赖 CANN 编译链和torch_npu运行时的二进制 PyTorch 扩展包。
6. 构建命令与平台选择
所有算子通过 amct_ops/ops_build.sh 统一编译并打包为 wheel。基本用法:
cd amct_ops/ bash ops_build.sh [--soc <soc>] [<operator>]--soc用于指定目标平台,默认值为ascend910b:
bash ops_build.sh # 构建全部算子,默认平台 bash ops_build.sh --soc ascend910_93 # 构建全部算子,指定平台 bash ops_build.sh hifloat8_cast # 只构建指定算子,默认平台 bash ops_build.sh --soc ascend950 hifloat8_cast # 只构建指定算子,指定平台可选平台如下:
| SOC 参数 | 平台 | 说明 |
|---|---|---|
ascend910b | A2 | Ascend 910B1/B2/B3,默认值 |
ascend910_93 | A3 | Ascend 910_93 |
ascend950 | A5 | Ascend 950,需要 CANN 编译器支持dav-3510 |
从 amct_ops/ops_build.sh 可以看到,SOC 会进一步映射为 NPU 架构:
ascend910b) NPU_ARCH="dav-2201" ;; ascend910_93) NPU_ARCH="dav-2201" ;; ascend950) NPU_ARCH="dav-3510" ;;也就是说,ascend910b和ascend910_93共用dav-2201,而ascend950使用dav-3510。这一映射也体现在 amct_ops/hifloat8_cast/CMakeLists.txt 中:该算子只接受dav-2201或dav-3510两类NPU_ARCH。
对svd_quant,构建脚本还有单独分支:只有在--soc ascend950下才会编译;否则脚本会跳过该算子。这说明svd_quant的平台适用范围与hifloat8_cast、hifloat4_cast不完全相同,构建时应注意目标 SOC。
7. 构建产物与 wheel 命名
构建产物位于:
dist/amct_ops-1.0.0-cp*-cp*-linux_<arch>.whl其中<arch>由构建主机架构决定,通常为x86_64或aarch64。wheel 文件名中的两个cp*分别表示 Python 实现/版本标签和 ABI 标签。例如cp311-cp311表示该 wheel 面向 CPython 3.11,并依赖 CPython 3.11 ABI;linux_<arch>表示构建主机平台架构。
amct_ops/setup.py 配置了固定包名amct_ops和版本1.0.0,并通过find_packages(where='staging')从staging/收集 Python 包。脚本还会扫描staging/amct_ops/<sub>/下的.so文件,将其作为package_data打进 wheel。
BinaryDistribution类的作用是强制生成平台相关 wheel,因为amct_ops内含编译后的.so文件,不是跨平台纯 Python 包。因此实际安装时需要注意 Python 版本、CPU 架构、目标 NPU 平台和 CANN 环境是否匹配。
8. 安装与使用示例
wheel 生成后,使用pip安装:
pip install dist/amct_ops-*.whl安装后有两种调用方式。
8.1 模块导入方式
# 方式一:模块导入(有 IDE 补全和文档字符串) from amct_ops.hifloat8_cast import encode_to_hifloat8, decode_from_hifloat8 y = encode_to_hifloat8(x) # FP16/BF16 → uint8 z = decode_from_hifloat8(y) # → bfloat16(默认) z = decode_from_hifloat8(y, torch.float16) # → float16这种方式更接近普通 Python 库的使用体验。amct_ops顶层包由 amct_ops/ops_init.py 在打包阶段复制为__init__.py,其中声明了可用子模块:
__all__ = ['hifloat8_cast', 'hifloat4_cast'] from . import hifloat8_cast from . import hifloat4_cast因此import amct_ops后即可通过amct_ops.hifloat8_cast、amct_ops.hifloat4_cast访问对应子包。
8.2 torch.ops.amct 方式
# 方式二:torch.ops.amct(与其他 NPU 算子风格一致) import amct_ops.hifloat8_cast # 触发 .so 加载 torch.ops.amct.encode_to_hifloat8(x) torch.ops.amct.decode_from_hifloat8(y, torch.float16)这种方式与 PyTorch 自定义算子注册机制一致。底层 C++ 扩展通过TORCH_LIBRARY_FRAGMENT(amct, m)注册算子 schema,再通过TORCH_LIBRARY_IMPL(amct, PrivateUse1, m)注册 NPU 后端实现。因此torch.ops.amct.<op>能解析成功的前提,是对应的.so已经被加载。
从 amct_ops/hifloat8_cast/python/hifloat8_cast/init.py 可以看到,Python 包导入时会定位同目录下的libhifloat8_cast_ops.so,并调用:
torch.ops.load_library(_lib_path)这正是 README 中“import amct_ops.hifloat8_cast触发.so加载”的具体实现。
9. Python 内省与文档查看
README 提供了两种 Python 内省方式:
import amct_ops help(amct_ops) # 查看所有子模块及接口列表 import amct_ops.hifloat8_cast help(amct_ops.hifloat8_cast.encode_to_hifloat8) # 查看单个函数签名和文档amct_ops/ops_init.py 的模块文档列出了amct_ops.hifloat8_cast与amct_ops.hifloat4_cast的函数说明,并给出快速示例:
import torch from amct_ops.hifloat8_cast import encode_to_hifloat8, decode_from_hifloat8 x = torch.randn(1024, dtype=torch.float16, device="npu") y = encode_to_hifloat8(x) # → uint8, same shape z = decode_from_hifloat8(y) # → bfloat16, same shape这类内省方式适合在不确定可用接口时快速检查包结构,也方便在 Jupyter、脚本或 IDE 中查看函数签名。
10. 已知 CMake 告警
使用 pip 安装的 PyTorch 构建时,find_package(Torch)可能输出如下告警:
static library kineto_LIBRARY-NOTFOUND not found.amct_ops/README_en.md 说明该告警来自 PyTorch 自带的TorchConfig.cmake,表示未找到 Kineto profiler 的静态库。当前amct_ops算子不依赖 PyTorch profiler / Kineto 能力;只要 CMake configure、编译和链接成功,该告警可以忽略。
这一判断与 CMake 链接配置相符:amct_ops/hifloat8_cast/CMakeLists.txt 中链接的是torch_npu、tiling_api、ascendcl、ascendc_runtime等 CANN / torch_npu 相关库,并未显式依赖 Kineto profiler。
11. 新增算子的目录结构
如果要新增算子,amct_ops/README_en.md 要求参照现有算子目录,例如hifloat8_cast/:
<new_operator>/ ├── op_kernel/ # Ascend C kernel(.cpp + tiling.h) ├── op_extension/ # PyTorch C++ binding(host stub 调用 + TORCH_LIBRARY 注册) ├── python/<pkg>/ # Python 接口(__init__.py) ├── CMakeLists.txt # 独立编译入口 └── README.md # 算子说明文档各部分职责如下:
| 目录 / 文件 | 职责 |
|---|---|
op_kernel/ | Ascend C kernel 实现,通常包含 kernel.cpp和 tiling 头文件 |
op_extension/ | PyTorch C++ 绑定,负责 host stub 调用和TORCH_LIBRARY注册 |
python/<pkg>/ | Python 包入口,包含__init__.py和可选封装函数 |
CMakeLists.txt | 独立 CMake 编译入口,生成该算子的.so |
README.md | 算子说明文档 |
从 amct_ops/hifloat8_cast/CMakeLists.txt 可以看到典型编译目标会由op_kernel/和op_extension/下的源文件共同构成共享库,例如:
add_library(hifloat8_cast_ops SHARED op_kernel/hifloat8_cast_kernel.cpp op_extension/hifloat8_cast_torch.cpp op_extension/register.cpp )其中 kernel 源文件被标记为ASC语言:
set_source_files_properties(op_kernel/hifloat8_cast_kernel.cpp PROPERTIES LANGUAGE ASC)这说明 Ascend C kernel 与 C++ 绑定在同一个 CMake target 下统一编译,最终形成可被 PyTorch 加载的.so。
12. 新增算子后的自动打包机制
README 指出:新增算子后无需修改 amct_ops/ops_build.sh 或 amct_ops/setup.py,构建脚本会自动发现<op>/python/<pkg>/目录并打包。
这一点可以从 amct_ops/ops_build.sh 的collect_op()函数得到印证。它会遍历<op>/python/下的每个子包目录,复制.py文件到staging/amct_ops/<pkg>/,如果<op>/build/中已经生成.so,也会一并复制:
find "$pkg_dir" -maxdepth 1 -name "*.py" -exec cp {} "$dst/" \; [ -d "$op_dir/build" ] && find "$op_dir/build" -maxdepth 1 -name "*.so" -exec cp {} "$dst/" \;随后 amct_ops/setup.py 通过find_packages(where='staging')收集 Python 包,并扫描staging/amct_ops/中的.so文件作为package_data。因此新增算子的关键是:
- 在
amct_ops/<new_operator>/下提供完整目录结构; - 保证
<new_operator>/CMakeLists.txt能编译出.so; - 在
<new_operator>/python/<pkg>/下提供 Python 包入口; - 运行
bash ops_build.sh或指定算子名构建,让脚本自动完成 staging 和 wheel 打包。
README 还提醒:正式测试应放在tests/amct_ops/下,避免把构建产物、性能脚本或依赖额外参考实现的对比工具放入算子源码目录。
13. 命名空间约束:所有算子注册到 amct
amct_ops/README_en.md 对命名空间有明确约束:所有算子必须注册到amct命名空间。这样与amct_ops包名保持一致,也便于调用方区分 AMCT 自定义算子与torch_npu上游算子。
C++ 侧需要同时完成 schema 注册和后端实现注册:
TORCH_LIBRARY_FRAGMENT(amct, m) TORCH_LIBRARY_IMPL(amct, PrivateUse1, m)Python 侧有两种访问方式:
torch.ops.amct.<operator_name> from amct_ops.xxx import <operator_name>新增算子前,应先检查torch.ops.amct中是否已存在同名算子,避免命名冲突。amct命名空间是amct_ops包在 PyTorch 自定义算子体系中的统一入口,新增算子如果偏离该命名空间,会破坏 README 所描述的调用约定。
14. 使用建议与排查要点
基于 README 和源码实现,实际使用amct_ops时可以从以下几点入手。
14.1 环境检查
构建前至少确认:
ASCEND_HOME_PATH已设置;- CANN
set_env.sh可被加载; - 主机 CPU 架构为
x86_64/amd64或aarch64/arm64; - 目标 SOC 在
ascend910b、ascend910_93、ascend950中; - PyTorch 版本与
torch_npu版本匹配,且满足 README 的版本约束。
14.2 构建产物检查
构建完成后,建议检查:
dist/amct_ops-*.whl是否生成;- wheel 文件名中的 Python 版本标签和 ABI 标签是否与当前环境一致;
- wheel 内容是否包含各算子子包和
.so文件; - 目标平台是否为预期 SOC 对应的
dav-2201或dav-3510。
14.3 接口调用检查
运行期建议先做最小验证:
import torch import amct_ops.hifloat8_cast x = torch.randn(1024, dtype=torch.float16, device="npu") y = torch.ops.amct.encode_to_hifloat8(x) z = torch.ops.amct.decode_from_hifloat8(y) print(y.dtype, y.shape) print(z.dtype, z.shape)如果出现torch.ops.amct无法解析的问题,优先检查对应子包是否已被导入、.so是否已加载、wheel 是否与当前 Python / CPU 架构 / CANN 环境匹配。
15. 小结
amct_ops是 AMCT 仓库中专门承载昇腾 NPU 自定义算子的模块。它把 Ascend C kernel、C++ PyTorch 绑定、Python 接口和 CMake 构建配置按算子目录组织起来,再通过 amct_ops/ops_build.sh 统一编译,并打包成包含.so的 wheel。对上层 Python 用户来说,它提供amct_ops.<op>与torch.ops.amct.<op>两类接口;对算子开发者来说,它要求新增算子遵循固定目录结构、注册到amct命名空间,并将测试放入 tests/amct_ops/ 下。理解这套约定,是正确使用和扩展amct_ops的基础。
【免费下载链接】amctAMCT是CANN提供的昇腾AI处理器亲和的模型压缩工具仓。项目地址: https://gitcode.com/cann/amct
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考