ops-memory:CANN Runtime 的 Tensor 内存管理

推理过程中显存的管理比 CPU 内存复杂得多。几十 GB 的模型参数、动态增长的 KV Cache、频繁创建销毁的中间激活值------全都在 NPU 的几十 GB 显存里竞争空间。

CANN 的 ops-memory 仓库负责 Tensor Buffer 的分配、回收和复用。它不是直接调 aclrtMalloc------它在上面封装了一整套内存池和生命周期管理机制。


为什么 Tensor Memory 很关键

一个 Decoder Block 的前向计算需要分配的 Tensor:

  1. 输入激活:[B, n, d]------8MB(B=1, n=4096, d=4096)
  2. Q 投影输出:[B, n, d]------8MB
  3. K 投影输出:[B, n, d]------8MB
  4. V 投影输出:[B, n, d]------8MB
  5. Attention Score:[B, H, n, n]------32MB
  6. Context:[B, n, d]------8MB
  7. FFN 中间激活:[B, n, 4d]------32MB
  8. 残差输出:[B, n, d]------8MB

总计约 112MB------一个 Block。40 个 Block 就是 4.5GB。这只是前向计算,训练的反向还要多一倍。

如果每次推理都重新分配这些 Tensor,分配+回收的开销可能占推理延迟的 5-10%。ops-memory 的优化是让 Tensor 复用------不同 Block 的同一层 Tensor 可以共享同一块显存。


Memory Pool 机制

ops-memory 的 Memory Pool 是一套多级分配器:

复制代码
进程级全局池(aclrtMalloc 的底层)
  └── 模型级池(当前加载的 OM 模型独占)
       └── Stream 级本地池(每个 Stream 独立加速)

全局池 在 CANN Runtime 初始化时从 NPU 显存中划走一大块。后续的 aclrtMalloc 都从池里分配------不需要跟内核交互。全局池的默认大小是 NPU 显存的 80%,剩余留给驱动和其他系统用途。

模型级池 在模型加载时创建。aclmdlLoadFromFile 加载 OM 模型时,GE 已经算出了模型需要的最大显存------模型级池的大小就是 GE 算出的最大值。模型级池从全局池中划分。

Stream 级本地池 在 Stream 创建时分配。aclrtCreateStream 创建 Stream 时可以指定 Stream 本地池的大小。推理时频繁创建销毁的临时 Tensor 从 Stream 本地池分配------不需要加锁(同一个 Stream 内的访问是单线程的)。


DMA 与 Buffer 对齐

DMA 的地址对齐要求比 CPU 严格------Buffer 的起始地址需要按 64 字节或 256 字节对齐。ops-memory 在分配 Buffer 时自动对齐。对齐方式由上层的 Tensor 格式决定:ND 格式用 64 字节对齐,NZ 格式用 256 字节对齐。

对齐浪费的显存空间通常很小(平均每个 Buffer 浪费 0.1-0.5%),但不符合对齐的 Buffer 会让 DMA 搬运退化到逐字节模式------带宽从 200GB/s 降到 10GB/s。


大模型中的显存优化

LLaMA-13B 推理中 ops-memory 的显存优化:

KV Cache 的增量分配。 Prefill 阶段结束后 KV Cache 已经满负荷。解码阶段每次追加一个 Token 的 K/V,ops-memory 不做整块重新分配------Cache 在 Prefill 时已经预分配了 max_length 的空间,解码阶段只更新偏移指针。

中间激活的复用。 LayerNorm 的 mean 和 var 在算完后立即释放。Attention Score 矩阵在第二阶段的矩阵乘结束后立即释放。ops-memory 的引用计数跟踪每个 Tensor 的使用次数------引用归零时返回内存池。

Memory Pool 的碎片管理

显存碎片是大模型推理中的隐蔽问题。KV Cache 在不同 Block 之间频繁分配和释放后,显存中会出现大量空闲但碎片化的 Block 间隙------总量够用但无法分配一个连续的 Buffer。

ops-memory 的碎片整理策略:当某个 Stream 的本地池碎片率超过 30% 时,触发碎片整理------把池中的空闲 Block 合并成更大的连续区域。整理过程需要暂停当前 Stream 的推理------约 50-100μs 的暂停。碎片整理的频率控制很关键:太频繁浪费延迟,太晚碎片化导致 OOM。

Runtime 的显存监控

ops-memory 通过 Runtime 的监控接口暴露显存使用指标:

  • total_memory:NPU 总显存
  • used_memory:当前分配量
  • pool_utilization:池利用率的百分比
  • fragmentation:碎片率

推理服务可以通过这些指标判断是否需要触发碎片整理或调整 Batch 大小。CANN 的 Runtime API 中 aclrtGetMemInfo 返回当前显存使用情况------推理框架可以定时调用这个接口做动态显存管理。

参考仓库

ops-memory 内存管理库

CANN Runtime

相关推荐
CC数学建模7 分钟前
2026年第十六届APMCM 亚太地区大学生数学建模竞赛(中文赛项)赛题B题:高性能芯片热管理系统的优化问题完整思路、代码、模型、文章,全网首发高质量分享!
python·算法·数学建模
爱睡懒觉的焦糖玛奇朵16 分钟前
【视觉检测之人员奔跑检测算法开发思路】
人工智能·python·深度学习·算法·yolo·视觉检测
05候补工程师21 分钟前
【408考研复习】数据结构核心笔记:字符串模式匹配与内部排序算法全解析
数据结构·经验分享·笔记·考研·算法·排序算法
阿文的代码库24 分钟前
浅谈:无向图的欧拉回路
算法
-Thinker25 分钟前
AI 算法核心原理与实现
人工智能·算法·机器学习
Eloudy27 分钟前
最小权重完美匹配(MWPM)与表面码纠错
算法·量子计算
-森屿安年-28 分钟前
62. 不同路径
算法·动态规划
学计算机的计算基40 分钟前
Codex CLI vs Claude Code 全方位对比:设计哲学与用户体验深度解析
算法
欧阳x天1 小时前
八大排序算法(C语言实现)
数据结构·算法·排序算法
爱睡懒觉的焦糖玛奇朵1 小时前
【从视频到数据集:焦糖玛奇朵的魔法工具Dataset Cleaner】
人工智能·python·学习·算法·yolo·音视频