English | 中文版
1. 背景:NPU 编程的现状与挑战
为什么关注内存安全?
在异构计算领域,GPU/NPU 编程长期以来依赖 C/C++ 生态。CUDA、OpenCL、SYCL 等框架虽然功能强大,但继承了 C/C++ 的所有内存安全问题:悬垂指针、缓冲区溢出、数据竞争、资源泄漏。这些问题在异构环境中尤为棘手——设备内存与宿主内存的交互增加了额外的复杂性。
一次典型的 NPU 编程失误可能表现为:
// C++ AscendC: 忘记释放设备内存 → 内存泄漏
void* devPtr;
aclrtMalloc(&devPtr, size, ACL_MEM_MALLOC_HUGE_FIRST);
// ... 使用 devPtr 做计算 ...
// 如果这里发生异常,aclrtFree 永远不会被调用
aclrtFree(devPtr);
Rust 的所有权系统和 RAII(资源获取即初始化)模式能够在编译期消除这类问题。这正是 ascend-rs 项目的核心动机。
开源生态现状
目前,异构计算的内存安全编程领域已有一些探索:
| 项目 | 目标硬件 | 方法 | 状态 |
|---|---|---|---|
| rust-cuda | NVIDIA GPU | Rust → PTX 编译,CUDA 安全绑定 | 不再活跃 |
| rust-gpu | GPU (Vulkan) | Rust → SPIR-V 编译 | 活跃 |
| krnl | GPU (Vulkan) | 安全的 GPU 计算内核 | 活跃 |
| cudarc | NVIDIA GPU | CUDA 运行时安全绑定 | 活跃 |
| ascend-rs | 华为昇腾 NPU | Rust → MLIR → NPU 编译,ACL 安全绑定 | 开发中 |
可以看到,昇腾 NPU 生态中,ascend-rs 是目前唯一一个尝试同时在宿主机端和设备端实现 Rust 内存安全编程的项目。 这填补了 Ascend 生态的一个重要空白。
ascend-rs 项目架构
ascend-rs 采用三层架构:
graph TD
A["应用层<br/>用户的 Rust 程序"] --> B["宿主机 API 层<br/>ascend_rs + ascend_sys<br/>RAII 安全封装"]
A --> C["设备运行时层<br/>ascend_std + rustc_codegen_mlir<br/>#![no_core] 运行时 | MLIR 代码生成后端"]
B --> D["CANN SDK · C/C++ 底层库<br/>ACL Runtime · AscendCL · bisheng · bishengir · HIVM"]
C --> D
宿主机 API 层通过 bindgen 自动生成 FFI 绑定,并在其上构建安全的 Rust 封装:Acl、Device、AclContext、AclStream、DeviceBuffer<T> 等,利用生命周期系统确保资源使用的正确顺序。
设备运行时层更具创新性:它包含一个自定义的 rustc 代码生成后端,将 Rust 代码编译为 MLIR。之后,mlir_to_cpp 翻译步骤将 MLIR 转换为带有 AscendC API 调用的 C++ 源码,再由 bisheng(CANN C++ 编译器)编译为 NPU 可执行二进制——昇腾 910B 和 310P 均采用这条路径。这条 MLIR-to-C++ 路径提供了完整的 AscendC 特性支持——DMA 操作、向量指令、流水线屏障和 TPipe 基础设施。翻译器识别 MLIR 中的 ascend_* 函数调用,并生成相应的 AscendC 向量操作。