用户层的推理代码经过 AscendCL → GE → Runtime 三层后,最后一步是 driver——CANN 的硬件驱动层。Driver 不参与推理逻辑,不管图优化,不管理算子——它的唯一职责是把 Runtime 提交的任务真正送给 NPU 硬件执行。

这句话意味着 driver 是 CANN 软件栈的最底层,也是性能损耗最小但最容易被忽略的一层。


AI 推理为什么依赖驱动

Runtime 生成的 Task 是一个软件描述:Kernel 句柄、输入输出地址、Stream ID。但 NPU 不懂软件——它只认识寄存器写、中断信号、DMA 描述符。

Driver 的职责是搭桥。它把 Runtime 的 Task 描述翻译成 NPU 硬件能理解的命令序列:

Runtime Task → driver → NPU 寄存器写入 → NPU 执行
               ↑               ↑
           软件翻译        硬件指令
  • 把 Kernel 句柄转成 AI Core 的指令加载地址
  • 把 Tensor 地址转成 DMA 搬运的目标地址
  • 把 Stream ID 转成硬件调度器的队列编号

这个过程看起来简单,但 driver 的处理效率直接决定了推理的延迟下限——每次 Task 提交的 driver 层延迟约 2-5μs。在解码阶段每步都要 Launch 几十次 Task 的场景下,driver 开销累加起来不可忽视。


Driver 如何连接 Runtime 与硬件

Driver 处在 CANN 软件栈的最底层,连接 Runtime 和硬件:

Runtime:
  Task 列表(Kernel 句柄、地址、Stream)
    ↓ 通过 ioctl 或 shared memory 传递给 driver
Driver:
  ┌─────────────────────────────────────┐
  │ Task 解析:解析 Kernel 类型和参数    │
  │ 地址映射:把虚拟地址转成物理地址     │
  │ DMA 编排:配置 DMA 搬运参数         │
  │ 任务提交:写硬件寄存器,触发执行     │
  └─────────────────────────────────────┘
    ↓ 硬件执行
NPU 硬件:
  AI Core 加载 Kernel,执行计算
  DMA 引擎搬运数据
  DVPP 模块处理图像

Driver 跟硬件的通信通过两种方式:

  • MMIO(Memory-Mapped IO):读写 NPU 的特定内存地址来配置硬件寄存器
  • Doorbell:写入特定内存地址触发 NPU 开始执行

DMA 为什么重要

DMA(Direct Memory Access)是 NPU 上最繁忙的硬件组件。推理过程中大部分时间不是在计算,而是在等数据从 DDR 搬到片上 Cache。

每次 GM→L1 的数据搬运都走 DMA。Transformer 推理时,一个 Attention 计算涉及的 DMA 传输:

  • K 矩阵从 DDR 搬到 L1:约 8MB
  • V 矩阵从 DDR 搬到 L1:约 8MB
  • Q 矩阵从 DDR 搬到 L1:约 8MB
  • 结果从 L1 写回 DDR:约 8MB
  • 32MB 的总搬运量 ÷ 带宽 200GB/s = 约 160μs

DMA 的传输效率(带宽利用率)主要取决于数据连续性和对齐方式。Driver 在编排 DMA 时会把多个不连续的搬运任务合并成一次连续的 burst 传输。ops-tensor 的 NZ 格式降低了 DMA 的连续性,但 driver 层会在可能的情况下尽量做地址连续性优化。


昇腾 Device 管理机制

Driver 管理 NPU Device 的生命周期。多卡场景中每个 Device 独立管理自己的执行资源。

Driver 在多卡场景中的几个关键职责:

设备发现与初始化。 系统启动时 driver 扫描 PCIe 总线上的 NPU 设备,为每个 Device 分配设备号和资源。npu-smi info 读到的信息就来自 driver。

显存管理。 Runtime 的 aclrtMalloc 最终调用 driver 的显存分配接口。Driver 维护 NPU 显存的页表,把 NPU 的物理显存映射到进程的虚拟地址空间。

上下文切换。 多进程共享 NPU 时,driver 负责进程上下文的切换——保存当前进程的 NPU 执行状态,加载下一个进程的状态。


大模型推理中的驱动瓶颈

大模型推理场景对 driver 的挑战跟小模型不同。

Kernel 加载延迟。 模型越大,Kernel 的二进制体积越大(融合后的算子包含更多指令)。Driver 把 Kernel 加载到 AI Core 指令缓存的时间跟 Kernel 大小近似线性。LLaMA-70B 的融合算子 Kernel 可能比 ResNet-50 的大 10 倍。如果 Kernel 缓存命中失败(比如动态 Shape 触发新 Kernel 加载),Driver 的加载时间可能从 5μs 弹到 50μs。

显存映射管理。 Paged KV Cache 的引入增加了显存管理的复杂度。Driver 需要管理更多更细粒度的显存页,页表的查询和更新频率大幅上升。CANN 8.0 之后 driver 对 Paged Attention 场景做了页表缓存的优化——经常访问的页表项缓存在硬件中,不用每次都去系统内存查。

多 Stream 调度。 多 Stream 场景下 driver 需要协调多个 Stream 的 Task 同时提交到 AI Core。如果两个 Stream 的 Task 都要求同一个 AI Core,driver 要判断是否分时执行还是分发到不同的 Core。

Driver 的批处理优化

Driver 对推理性能影响最大的是 Task 提交的批处理能力。每次 Task 提交都需要执行 ioctl 系统调用——从用户态切换到内核态,这个切换的固定开销约 1-2μs。

独立提交 100 个 Task → 100 次 ioctl → 100-200μs 的切换开销。

Batch Launch 提交 100 个 Task → 1 次 ioctl → 2-4μs 的切换开销。

CANN 的 GE 在生成 Task 列表时已经把任务按 Stream 和依赖关系分好组。Driver 的 Batch Launch 接口一次接收一组 Task,在驱动内部逐个配置硬件寄存器并触发执行。ioctl 次数从 N 次降到 1 次。

Kernel 缓存

Driver 对 AI Core 加载的 Kernel 二进制做缓存。同一个 Kernel(同一种算子、同一组参数)在当前模型生命周期内只加载一次,之后的执行复用缓存的指令。

缓存的 key 是 Kernel 的 hash 值(由算子类型和参数的 hash 计算得出)。GE 在 Task 描述中包含了这个 hash——Driver 根据 hash 查找缓存,命中直接跳到执行,不命中再触发硬件加载。

参考仓库

CANN driver 驱动层

CANN Runtime

Logo

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

更多推荐