惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
博客园 - 叶小钗
H
Help Net Security
P
Privacy & Cybersecurity Law Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Help Net Security
Help Net Security
Hugging Face - Blog
Hugging Face - Blog
D
Docker
Security Archives - TechRepublic
Security Archives - TechRepublic
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
V2EX - 技术
V2EX - 技术
人人都是产品经理
人人都是产品经理
L
LINUX DO - 最新话题
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Google Online Security Blog
Google Online Security Blog
博客园 - 聂微东
WordPress大学
WordPress大学
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Latest news
Latest news
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
B
Blog RSS Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Blog — PlanetScale
Blog — PlanetScale
C
Cisco Blogs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Know Your Adversary
Know Your Adversary
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
爱范儿
爱范儿
Webroot Blog
Webroot Blog
宝玉的分享
宝玉的分享
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
Recent Commits to openclaw:main
Recent Commits to openclaw:main
The Register - Security
The Register - Security
Simon Willison's Weblog
Simon Willison's Weblog
A
Arctic Wolf
Scott Helme
Scott Helme
The Last Watchdog
The Last Watchdog
Y
Y Combinator Blog
Last Week in AI
Last Week in AI
S
Securelist
Cloudbric
Cloudbric
G
GRAHAM CLULEY
M
MIT News - Artificial intelligence
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
Visual Studio Blog
S
Schneier on Security
Engineering at Meta
Engineering at Meta

Пусть этот камень будет более крепким, чем человек

【琐记】烟火与尘埃 【Triton】Triton实现矩阵乘 【LLM推理加速】FlashAttention 【LLM推理加速】PagedAttention 【LLM推理加速】Online Softmax LLM基础知识【1】 Transformer模型 【AI编译】LayerGroup Tiling Tile的疑惑和思考 【AI编译】深度优先的Tile调度,万事大吉? 【AI编译】多级流水线Tile调度策略 【CUDA C++】GPU内存使用【3】 【CUDA C++】GPU存储【2】 【CUDA C++】GPU基本介绍【1】 【00】0序章-不受欢迎的来客 【转载】我来了——持续低熵 【Halide】调度优化【2】 【感想】写作进度报告5 【Halide】调度优化【1】 【转载】北大中文男足战报2 【BYOC】TVM切分子图 【转载】北大中文男足战报1 【AI编译】张量生命周期管理 SystemC 用寄存器同步建模方法 【脉动阵列】脉动阵列类型 【im2col】AScend conv accelerate 【感想】写作进度报告4 【BYOC】TVM添加自定义编译器 ccompiler 【感想】写作进度报告3 【Tengine】推理流程脑图【2】 【Tengine】推理流程脑图【1】 【NCNN】学习ncnn模型转换 【编译器】使用llvm编译自定义语言【3】编译 object 【编译器】使用llvm编译自定义语言【2】转llvm IR 【编译器】使用llvm编译自定义语言【1】构建AST 【AI编译】如何进行内存分配 【感想】写作进度报告2 【AI编译】layer-group之后如何tiling 【AI编译】如何进行layer-group 【量化】连续卷积层首尾量化的可行性 【Gemm】内存对齐 【gemm】Gemm计算加速 【TVM】通过代码学习编译流程【5】FuseOps 【TVM】通过代码学习编译流程【6】CodeGen 【TVM】通过代码学习类【3.5】Pass 【TVM】通过代码学习编译流程【4】BuildRelay 【AI编译】Tiling操作能优化什么时间 【TVM】通过代码学习编译流程【3】模型编译 【TVM】通过代码学习编译流程【2】模型转换 【TVM】通过代码学习编译流程【1】必要知识 【感想】写作进度报告1 【Winograd】卷积加速算法原理及实现 SystemC 等待异步事件解决方案 【TVM】Python脚本实现模型编译和保存 【推理引擎】常见AI推理框架 【3D建模】T110E3卡迪夫蓝调皮肤模型 【TVM】C++部署运行TVM 【推理引擎】NCNN和Tengine量化推理逻辑对比 【3D建模】IS-7攻城锤流纹岩皮肤展示 【TVM】根据例子走通代码库 博客汇总目录 【Im2Col】卷积加速算法【2】NHWC 【Im2Col】卷积加速算法【1】 NCHW openBlas库的安装与简单使用 C语言工程调用Cpp库解决方案 foo Hello World
【AI编译】Cache缓存地址映射
Post author: XianMu@Пусть этот камень будет более крепким, чем ч · 2026-01-25 · via Пусть этот камень будет более крепким, чем человек

# 前言

软件离不开硬件,硬件也离不开软件啊。作为 “根本不了解硬件” 的我,也不得不去了解和学习硬件的基本概念。本篇文章 Cache 缓存设计的相关内容。

内容和图源自书籍《AI 处理器硬件架构设计》,请参考。

错误在所难免,还望不吝赐教。

NPU 的 Cache 设计与其通用 CPU/GPU 的 Cache 有很大不同,核心目标是满足高吞吐、可预测的流式数据访问,而非处理通用计算的随机访问。
NPU L2 Cache 的定位:

  • 1. 容量大,带宽极高:作为片上 “数据水库”,为多个计算核心(Tensor Core/Matrix Unit)或向量单元提供数据。8MB 是一个常见且合理的尺寸。
  • 2. 服务于规律的数据搬运:NPU 的计算模式通常是 “权重加载 + 输入数据滑动窗口” 或 “大型矩阵分块计算”。数据访问模式在编译期就可高度预测。
  • 3.Bank 冲突是主要敌人:并行计算单元(例如 256x256 的矩阵乘)会同时请求海量数据(如一整行或一整列)。如果 Bank 规划不当,多个请求落在同一个 Bank,就会造成严重的排队延迟,极大降低算力利用率。
  • 4. 简化控制逻辑:相较于 CPU Cache 复杂的替换算法(如 LRU),NPU L2 的替换策略更直接,常由编译器进行显式管理

# Cache 主要有三种映射方式

# 1. 高位交叉(High-Order Interleaving)

现代 NPU/GPU 中的 Cache(如 L2 Cache)在物理上通常被组织为一个由多个独立存储体(Bank)组成的阵列,每个 Bank 拥有自己的地址解码、数据读写端口和控制逻辑,Bank 之间可以并行工作。每个 Bank 在每个时钟周期内,都能完成对其内部一整行(即一个 Cache Line)数据的读取或写入。

图 8-5

上图是高位交叉的地址映射示意图。

地址可以分为 3 个域段: 最低域段为 Offset 域段, 用于指示 Bank 每行内地址偏移; 中间域段为 RAM Address 域段,用于指示 RAM 的行数; 最高域段为 Bank 域段, 指示访问的是哪个 Bank。

这种映射方式,是将数据地址的高位来生成 Bank ID,而数据一般是连续的,所以数据倾向于存储在同一个 Bank 中。

这种映射逻辑下,同一个 Bank 内的地址是连续的:

bank 内连续

可以很容易联想到:如果程序访问一段连续数据(如一个张量),所有请求都打到同一个 Bank- 其他 Bank 闲置,会造成很大的带宽浪费。

# 2. 低位交叉(Low-order Interleaving)

图 8-6

上图是低位交叉的地址映射示意图。

这种映射方式是 取地址中间的几位(通常是行内偏移 Offset 之上的几位)作为 Bank ID。如 Bank ID = addr [13:8]。

地址可以分为 3 个域段: 最低域段为 Offset 域段, 用于指示 Bank 每行内地址偏移;中间域段为 Bank 域段, 指示访问的是哪个 Bank; 最高域段为 RAM Address 域段, 用于指示 RAM 的行数。
这时候一段连续的数据,其低位地址时连续变化的,因此倾向于被映射到 Cache 的同一行的不同 bank 中。

这种映射逻辑下,相邻 Bank 间同一行的 Cache line 的地址是连续的,跨 bank 连续 :

跨bank连续

可以很容易联想到:如果这时候程序访问一段连续数据(如一个张量),请求会分散到多个 bank 中,访问效率会高很多。

再进一步探讨下低位交叉:

低位交叉的 bank 冲突

为方便说明,假设每个访问源每个时钟周期只访问一个字节。 假设每列数据都在同一个 Bank 中, 整系统共 4 个 Bank, 每个 Bank 的宽度为 1B。source0 的起始地址为 1, 访问长度为 9B, source1 的起始地址为 12, 访问长度为 8B, source2 的起始地址为 22, 访问长度为 8B。

图 8-8

假设 source0~ source2 同时发起访存请求, 由于它们的起始地址在不同的 Bank 中, 因此三者之间可进行无冲突的并行访问。

图 8-9

假设 3 个访问源的起始地址都落在了同一个 Bank 中, 并且 3 个访问源是同时发起访存请求的, 则首个访问请求出现了 Bank 冲突。

这种情况下,假设轮询仲裁器的轮询顺序是 source0→source1→source2, 则 source1 会被反压一个时钟周期, source2 会被反压两个时钟周期。

但一般来说,这种访问的数据量都比较大,只在首次访问时进行的几个时钟周期的反压对整体的性能影响较小。

Bank 划分粒度对访存效率的影响

图 8-11

上图左边,假设整个地址空间分为两个 Bank, Bank 宽度为 2B,而右边,整个地址空间分为 4 个 Bank, Bank 宽度为 1B。有一个指令,希望读取 Cache 中的 2、 4、 11 和 13 四个地址。

由于第一种划分方式,Bank0 中的 4 和 13 不在同一行,需要两个周期才能访问。Bank1 中的 2 和 11 也需要两个周期才能访问得到。而 bank0 和 bank1 能够并行,所以整体需要两个周期。

第二种划分方式,四个 bank 都能并行,所以能够一个周期,将所有数据访问到。

所以,划分更多 bank 能够有更高的访问效率,但是 bank 更多也会带来布线、面积、成本上的困难。

图 8-12

上图是一个新的例子,有两个请求,source a 和 source b,分别访问 [14-21] 和 [38-45] 的数据,也不难分析,第一种划分方式,需要两个周期,而第二种方式,只需要一个周期。

# 3. 混合交叉

混合交叉的主流方案是 Bank Group(存储体分组)

混合交叉顾名思义,就是结合了高位交叉和低位交叉。

图 8-10

上图所示为混合交叉的地址映射示意图。
混合交叉将 Bank 划分为组(group),图中的每一列都是一组,组和组之间采用低位交叉,组内采用高位交叉。

例如假设有 64 个 bank,将其中 8 个 bank 划分成一组,那么就是 8 group * 8 bank。此时一段连续的数据,倾向于这样映射:第一块 128B 数据映射到 group0,第二块 128B 数据映射到 group1…… 第九块 128B 数据又映射到 group0 。而最终映射到 group0 的若干块数据,倾向于映射到同一个 bank,比如 bank5(每个 group 8 个 bank)。

将低位交叉改进为混合交叉,可能有以下原因:

1. 两级地址映射,减少排线复杂度和面积占用。(不懂硬件,我也不清楚)
2. 将冲突区域从全局变成局域。
纯低位交叉,所有的数据都会分散到 64 个 bank 中,当许多请求来访问各自的数据的时候,他们之间都会出现互相干扰,导致所有请求都无法及时完成,冲突域是全局的。而混合交叉,会将各个冲突域限制在所有 group 的某个 bank 中,比如这些请求都在访问所有 group 的第 2 个 bank,group 中的其他 bank 仍然处于可用状态。
就像一个大型体育馆被分成了 8 个独立的区域,一场骚乱最多只能让每个区域的一个入口堵塞,其他 7 个入口和区域内部的大部分空间仍然可用,从而保证了系统在最坏情况下仍有基本的通行能力和吞吐量。

# NPU Cache 设计选择

# 高位交叉相比低位交叉的缺点

1. 通用性与编程复杂度
高位交叉需要显式管理 Bank 分配,低位交叉能够自动分散。
NVIDIA CUDA、Google TensorFlow 等框架开发者更倾向于透明管理的方案。

2. 负载不均衡
高位交叉,运行过程中只有某个或某几个 bank 处于活跃状态,其他 bank 都未利用。
低位交叉,所有 bank 都处于活跃状态。

3. 动态调度困难
现代 NPU/GPU 支持:动态并行(Dynamic Parallelism)抢占式多任务(Preemptive Multitasking)实时推理(Real-time Inference)等
比如多模型并发执行: 基本没法为多个模型共同分配不冲突的 bank。
时间片 0:运行 ResNet-50(按高位交叉分配 Bank 0-31)
时间片 1:需要运行 BERT(但理想 Bank 0-31 被占用)
解决方案:要么等待,要么接受次优分配

4. 内存碎片化
高位交叉会导致严重的内部碎片

# 选择

从以上分析来开看,在通用、硬件自动管理的 GPU/NPU 缓存设计中,高位交叉是低效的。

但是!但是!而对于追求极致效率与确定性的专用 NPU 来说,类似高位交叉的内存管理可能更适用。

因为其核心不是传统缓存,而是一个可以由软件显式管理的统一缓冲区。
神经网络的计算图在编译时是完全已知、静态的。计算图和数据流在编译期完全确定、可预测。

“访存” 不是一个被动的、随机的读取过程,而是一个由编译器主动编排的、与计算精确同步的 “数据供给流水线”。

这种情况下,NPU 编译器要在神经网络模型编译的过程中,就要将可能存在并行的 Tensor 安排进不会产生冲突的 Bank 中。将数据连续存放在同一个 bank 中,就不再是效率瓶颈,而是通过微指令的流水线,获取可靠和稳定的数据供给,让数据搬出、计算同时、结果存储进行。这时候即使将 Tensor 分散在不同的 bank 中,也不会明显增加执行速度,还可能因为访存的潜在冲突和不确定性,拖累整个执行速度。

至于以上 “高位交叉相对于低位交叉的缺点” 中的其他问题,我也不清楚如何解决。也许,车到山前必有路呢?

一条大河波浪宽
风吹稻花香两岸
我家就在这岸上住
听惯了艄公的号子
看惯了船上的白帆

# 后记

我也不清楚以上内容有多少错误,有哪些不足,不过这些东西总会在前行的道路上逐渐出现,并得以解决。

本博客目前以及可预期的将来都不会支持评论功能。各位大侠如若有指教和问题,可以在我的 github 项目 或随便一个项目下提出 issue,并指明哪一篇博客,看到一定及时回复!