模型权重占了一部分显存KV Cache 又占了一部分。推理时真正把你显存打满的往往不是这两者而是中间 Tensor 的 Buffer——它们生得快、灭得快但同一时刻可能同时活着一大群。一次 Attention 前向至少产生七八个中间 TensorQKV 投影后的矩阵、Score 矩阵、Softmax 后的注意力权重、Attention 输出。FP16 下每个从几 MB 到几十 MB 不等。以 LLaMA-7B、SeqLen128K 为例——单层 Attention 的 Score 矩阵 [1, 32, 128K, 128K] 就是 32GB。几十层的中间 Tensor 加在一起显存不够用是常态。为什么不能每次推理都 mallocNPU 上acl.rt.malloc的延迟在 0.5ms-2ms 之间。如果一次推理产生 20 个中间 Tensor每个 malloc 后续 free显存管理开销就达到 10-40ms。而整次推理的矩阵运算可能只有 25ms。Runtime 的解决方案是 Memory Pool——初始化时预申请一大块显存内部做柔性切割。后续所有中间 Tensor 的分配都在 Pool 内部完成不再走驱动层的页表映射和内存管理单元。Pool 里找一块够大的空闲区域切下来延迟从 ms 级降到 μs 级。Buffer 的生命周期和复用Tensor缓存的关键不只是提前申请而是同一块内存给多个 Tensor 用。Runtime 在加载 OM 模型时就知道每个中间 Tensor 的生命周期——从哪个 Kernel 产生到哪个 Kernel 最后一次使用。把生命周期画在时间轴上Transformer Block 内 Tensor 生命周期简化 Timestep: 0 1 2 3 4 5 6 q_reshaped [] k_reshaped [] scores [] attn [] ffn_hidden [] ffn_output [] 不重叠的 Tensor 可以复用同一块 Buffer - q_reshaped 和 ffn_hidden 不重叠 → 复用 4MB - k_reshaped 和 attn 不重叠 → 复用 4MB - scores 和 ffn_output 不重叠 → 复用 32GB最大的单块复用最大不重叠集的算法跟会议室调度一样按结束时间排序贪心找下一个可用块。Buffer复用把峰值显存从全部 Tensor 之和降到最大不重叠集的面积。一个 Attention Block 里Buffer 复用率通常能达到 60-75%。零拷贝跨 Task 传递融合算子解决了单 Kernel 内部的 Buffer 问题但融合不到一起的 Kernel 之间还需要传数据。非融合场景下Kernel A 的输出写回 DDRKernel B 再从 DDR 读——一次 DDR 写回 一次 DDR 读入。Runtime 的 Buffer 管理可以在某些情况下实现零拷贝跨 Task 传递Kernel A 的输出 Buffer 和 Kernel B 的输入 Buffer 在 Pool 里分配为同一块物理地址。Kernel A 写完输出后不做任何事Kernel B 启动时直接从同一块 L1 空间读——省掉一次 DDR 写回和一次 DDR 读入。零拷贝 Buffer 传递 非零拷贝 Kernel A 输出 → 写回 DDR15μs Kernel B 启动 → 从 DDR 读入15μs 总开销30μs Kernel Launch 延迟 零拷贝 Kernel A 输出 → 留在 L1 Buffer Pool 同一地址 Kernel B 启动 → 从同一地址读 总开销仅 Kernel Launch 延迟显存分析LLaMA-7B 推理的 Buffer 去哪了Batch Size 4SeqLen 4096FP16Ascend 91064GB 显存显存项占用模型权重7B FP1614 GBKV Cache4 × 32 × 4096 × 128约 8 GB中间 Tensor Buffer峰值约 3.5 GB内存池碎片和预留约 2 GB合计约 27.5 GB3.5GB 的中间 Buffer 在 64GB 的卡上不算什么。但如果你跑 13B 模型权重 26GB SeqLen 128K 的 KV Cache约 32GB剩余空间不到 6GB被中间 Buffer 碎片吃光就是分分钟的事。参考仓库Runtime 运行时GE 图引擎CANN SamplesCANN 学习中心