driver:昇腾 NPU 的硬件执行通道
用户层的推理代码经过 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 查找缓存,命中直接跳到执行,不命中再触发硬件加载。
参考仓库
更多推荐




所有评论(0)