存储器层级结构

目录

一种通用的存储器层级结构

内存层级在实际工程中的核心矛盾与优化方向

性能与复杂性的矛盾

性能优化的核心原则

延迟和带宽的非均匀性

预测数据位置的困难

预取延迟取决于位置

[2020年代计算机系统(AMD Zen 4)的缓存、内存和外部存储层级结构](#2020年代计算机系统(AMD Zen 4)的缓存、内存和外部存储层级结构)

[有些 CPU 在 L3 和内存之间加 L4](#有些 CPU 在 L3 和内存之间加 L4)

从大容量存储向下叫分层存储,分在线/近线/离线

内存层级如何制约程序性能

性能墙与三维数组

[什么是 Memory Tiering?](#什么是 Memory Tiering?)

为什么需要?

[Linux 怎么实现 Memory Tiering?](#Linux 怎么实现 Memory Tiering?)

[1. 基于 NUMA 扩展](#1. 基于 NUMA 扩展)

[2. 用"热页"迁移](#2. 用"热页"迁移)

[3. 加权交错分配(Weighted-interleave)](#3. 加权交错分配(Weighted-interleave))

[放眼当今2026 年](#放眼当今2026 年)


当 CPU 需要数据时,如果高层(比如 Cache)里没有 ,就要去低层(比如内存) 取。因为低层慢,CPU 就得"干等",这叫等待延迟(latency)

为了减少这种等待,硬件会自动做以下事情:

  1. 预测:CPU 或内存控制器会"猜"你接下来可能需要什么数据
  2. 提前取 :不等 CPU 要,就提前把低层的数据搬到高层(比如从内存预取到 Cache)
  3. 填充缓冲区:把取来的数据先放到一个临时缓冲区里
  4. 发出激活转账信号:通知相关部件"数据准备好了,可以传输了"

这样做的好处是:等 CPU 真正需要数据时,数据已经在高层了,不用等。

一种通用的存储器层级结构

  • 内部------ 处理器寄存器和缓存。
  • 主接口------系统内存和控制卡。
  • 在线大容量存储------二级存储。
  • 离线散装储存------三级和离线储存。

存储层级在实际工程中的核心矛盾与优化方向

性能与复杂性的矛盾

我们加 Cache、加预取、加乱序执行,不就是为了更快吗?为什么反而说"增加复杂性会拖慢"?

原因在于:每一层额外的控制逻辑都会引入延迟。

举个例子:

增加的东西 带来的好处 付出的代价(延迟)
多级 Cache 命中率提高 每一级都要做标签查找(tag lookup)、一致性检查
预取器 提前把数据搬上来 需要额外的预测逻辑、可能污染 Cache
虚拟内存 程序隔离、方便 每次访存要多做一次**页表翻译(TLB 查找)**​

复杂性不是免费的。每增加一层抽象或控制,CPU 在"找到数据"之前就要多做一步判断。如果这个判断本身很耗时,反而会抵消掉"数据在高层"带来的好处。

性能优化的核心原则

这句话是整个内存层级设计的终极目标,也是程序优化的核心原则。

**"how far down"**​ 就是访问延迟的量级:

需要去到第几层 大概延迟(现代 CPU) 性能代价
L1 Cache ~1 ns 几乎无代价
L2 Cache ~3-4 ns 很小
L3 Cache ~10-20 ns 可接受
主内存 ~60-100 ns 已经很慢了
SSD ~100 μs 慢了 1000 倍
硬盘 ~10 ms 慢了 10 万倍

核心思想:每往下一层,延迟就跳一个数量级。所以:

  • 算法设计:尽量让数据留在高层(时间局部性 + 空间局部性)
  • 数据结构设计:紧凑排列,提高 Cache 命中率
  • 编程技巧:循环分块(loop tiling)、 prefetch 指令

性能 = 尽量在"高处"把事情做完

AI 训练里一次 HBM miss 去 NVMe 加载权重,等于白跑几百万次 FLOP 。所以 2026 年所有大模型推理优化(KV-cache 分页、PagedAttention、context memory offload)本质都是这句话的翻版:

别往下走,往下走一次,token 就少一半。

延迟和带宽的非均匀性

一个容易被忽略但极其重要的事实 :内存层级中,不同层的延迟和带宽差异巨大,且不能用一个统一模型描述。

延迟(Latency)= "拿到第一个字节要多久"

CPU 寄存器 → 0 周期

L1 Cache → 3-4 周期

L2 Cache → 10-12 周期

L3 Cache → 30-40 周期

主内存 → 200-300 周期

带宽(Bandwidth)= "每秒能搬多少数据"

L1 Cache → ~1-2 TB/s(非常高)

L2 Cache → ~500 GB/s

L3 Cache → ~200 GB/s

主内存 → ~50 GB/s(相对低很多)

"非均匀"的含义

  1. 延迟非均匀:从 L2 到 L1 可能只要多等 3 个周期,但从内存到 L3 要多等 200 个周期
  2. 带宽非均匀:高层带宽极高但容量极小,低层容量大但带宽反而受限(内存总线是共享的)
  3. 不同组件差异巨大:你不能说"Cache 延迟是 X",因为 L1、L2、L3 的延迟完全不同

工程意义 :写高性能程序时,必须针对具体哪一层来优化。比如:

  • 对 L1 友好的代码(小循环、连续访问)和对内存友好的代码(批量 DMA 传输)策略完全不同
  • 乱用 prefetch 可能反而降低性能,因为预取本身占用内存带宽

预测数据位置的困难

硬件和软件都面临的一个根本性难题你不知道数据现在"在哪一层"。

原因很复杂:

1. 硬件层面的不确定性

  • Cache 是硬件自动管理的,程序员不可见

  • 你写的变量可能在 L1、可能在 L2、可能已经被换出到内存、甚至可能被其他程序挤掉了

  • 多核 CPU 中,另一个核可能修改了数据,导致你这边的 Cache 行失效(Cache coherence)

2. 软件层面的不确定性

  • 操作系统可能把你的内存页换出到磁盘(swap)

  • 虚拟内存让物理位置对程序透明

  • 多线程/多进程共享资源,互相干扰

3. 后果很严重

  • 如果预测错了(以为在 L1,实际在内存),性能直接掉一个数量级
  • 预取器如果预测错了,不仅白忙活,还污染了 Cache(把有用的数据挤走了)

这就是为什么现代 CPU 花了大量晶体管在做分支预测 + 预取预测------本质上就是在赌"数据在哪"。

预取延迟取决于位置

  • 预取(prefetch)= 提前把数据从低层搬到高层
  • 从哪搬、搬到哪,决定了预取本身要花多少时间

具体来说:

预取场景 需要的时间 原因
从 L2 预取到 L1 ~10 周期 距离近,总线快
从内存预取到 L3 ~200 周期 要过内存控制器、DRAM 延迟
从 SSD 预取到内存 ~100 μs 要发起 I/O 请求、等磁盘寻道

关键工程含义

  1. 预取必须"提前足够多":如果你从内存预取需要 200 周期,那你至少要在 200 周期之前就发出预取请求,否则 CPU 还是得等
  2. 预取距离要匹配层级深度
    • 对 L1:提前几条指令就够了
    • 对内存:需要提前几百条指令甚至更早
    • 对磁盘:需要提前几毫秒(操作系统级的 readahead)
  3. 预取太早也有问题:数据可能被挤走(Cache 被其他东西填满了)

预取本身在 2026 年已经不是"CPU 指令"概念,而是 硬件预取 + OS 页迁移 + 运行时 offload 的三层叠加

2020年代计算机系统(AMD Zen 4)的缓存、内存和外部存储层级结构

有些 CPU 在 L3 和内存之间加 L4

  • 2026 年还有没有"L3 和内存之间的一层"?有,但换形态了
    • Intel :Meteor Lake / Arrow Lake 后提出 Adamantine L4 (封装内大缓存,Linux 补丁里叫 adamantine,128 MB--GB 级,给 iGPU/CPU 用);此外 CXL Type-3 内存扩展卡在系统软件眼里就是"L3 之下、DRAM 之下的另一层可缓存/可迁移内存"。
    • AMD :不叫 L4,而是 3D V-Cache 把 L3 堆到 96--128 MB/CCD(Zen 4 X3D、Zen 5 X3D),EPYC 上 L3 总量可达 768 MB+;本质是把"L3 和 DRAM 之间的缓冲"用堆叠实现,而不是另起 L4 名号。
    • GPU 侧 :NVIDIA Blackwell 的 Context Memory / HBM 侧 KV-cache 层 、Apple M 系列的 片上 SLC(系统级缓存) 也都是"L3 类结构之下、主存之上"的变形。

从大容量存储向下叫分层存储,分在线/近线/离线

类别 2026 定义 2026 典型介质
在线 Online 通电、挂载、立即可 I/O,TTD 通常 ≤ 80 ms(SNIA Emerald) 一直转的 HDD、SSD、NVMe、SAN/NAS、低延迟云盘
近线 Nearline 不立即可访问,但机器自动上线,无需人 MAID 盘阵、自动磁带库(机械臂)、**云 Nearline(GCS Nearline / S3 Glacier Instant)、近线 QLC SSD(NL SSD)**​
离线 Offline 必须人工干预才上线 手拔磁带、离线光盘、冷备份盘、手动装载的 LTO、Air-gapped 归档

内存层级如何制约程序性能

整个计算机体系结构里最经典的矛盾,叫 "内存墙"(Memory Wall)

大多数现代中央处理器(CPU) 的运算速度已经极快,以至于对绝大多数程序负载而言,性能瓶颈 在于内存访问的访问局部性CPU 高速缓存的效率,以及存储层级之间的数据传输效率 来源请求

因此,CPU 大部分时间都处于空闲状态,等待内存 I/O 完成。这有时被称为空间成本 :一个更大的内存对象更有可能溢出 "小而快" 的存储层级,转而需要使用 "大而慢" 的层级。由此产生的内存使用负载被称为压力(分别为寄存器压力、缓存压力和(主)内存压力)。

数据在更高层级缺失、需要从更低层级调取的情况,对应术语分别为:

  • 寄存器溢出(由寄存器压力导致:从寄存器溢出到缓存)
  • 缓存未命中(从缓存调取到主内存)
  • (硬)缺页异常(从真实主内存调取到虚拟内存,即大容量存储;无论实际存储技术是什么,通常都统称为 "磁盘")

现代编程语言 主要默认存在两层内存:主(工作)内存和大容量存储。例外是相对底层的汇编语言 ,以及 C 语言等高级语言中的内联汇编------ 在这些场景下可以使用 "预取" 指令提前加载缓存。

要充分利用内存层次结构,需要程序员、硬件和编译器的协作(同时也需要操作系统的底层支持):

程序员 (Programmer) 文件 I/O、数据布局、并行/线程亲和性

编译器 (Compiler) 寄存器分配、循环变换、prefetch 生成

硬件 (Hardware) Cache 协议、预取器、一致性、DDR/CXL 控制器

操作系统 (OS) 虚拟内存、页表、swap、NUMA/CXL tiering

性能墙与三维数组

通过调换三维数组里三层循环的顺序,你可以让程序从"飞快"变成"极慢",而这背后的原因完全取决于 CPU 缓存和局部性原理。

CPU 缓存之所以能加速程序,靠的是两个规律:

类型 含义 缓存如何利用它
时间局部性 刚访问过的数据,很快还会被访问 把数据留在缓存里,下次直接用
空间局部性 访问了地址 A,附近的数据很快也会被访问 一次加载一整块(Cache Line,通常 64 字节)到缓存

什么是 Memory Tiering?

传统上,我们以为"主内存"就是一块均匀的、速度一样的 DDR 内存 。但到 2026 年,这个假设已经不成立了

Memory Tiering = 把"主内存"按性能分成多个层级,像存储层级一样管理。

为什么需要?

因为现代系统里,"主内存"可能由完全不同的硬件混搭而成:

内存类型 延迟 带宽 成本 在系统中的角色
本地 DDR5 ~80 ns ~60 GB/s Tier 1(快层)
CXL 内存(插在 PCIe 槽上) ~250 ns ~40 GB/s Tier 2(慢层)
Optane/3D XPoint(已退场) ~150 ns ~低 历史方案
GPU 显存(HBM) ~10 ns ~3 TB/s 极高 加速器自有
NUMA 远端内存 ~140 ns ~共享 同 DDR 跨 socket

这些设备都出现在同一个物理地址空间里 ,操作系统看到的"内存"不再是均匀的了。所以需要像管理存储层级一样,把它们分层、迁移、调度

存储 Tiering(熟悉的):
热数据 → NVMe SSD(快、贵、小)
温数据 → SATA SSD(中)
冷数据 → HDD(慢、便宜、大)

内存 Tiering(新的):
热页 → 本地 DDR5(快、贵)
温页 → CXL 内存(中)
冷页 → NVMe(swap,极慢)

Linux 怎么实现 Memory Tiering?

1. 基于 NUMA 扩展

Linux 把每个内存设备看成一个 "CPU-less NUMA node"(没有 CPU 的 NUMA 节点):

传统 NUMA:

Node 0: CPU 0 + 本地 DDR

Node 1: CPU 1 + 本地 DDR

2026 内存 Tiering:

Node 0: CPU 0 + 本地 DDR5 (距离 = 最近)

Node 1: CPU 1 + 本地 DDR5 (距离 = 近)

Node 2: CXL 内存(无 CPU) (距离 = 较远,用"abstract distance"表示)

Node 3: 可能还有另一块 CXL (距离 = 更远)

"abstract distance" :不是物理距离,而是性能距离------延迟越高、距离越大。

2. 用"热页"迁移

Linux 内核通过 page fault 的频率来判断一个内存页是"热"还是"冷":

  • 经常被访问的页 → 热 → 迁移到快层(DDR5)
  • 很久没被访问的页 → 冷 → 迁移到慢层(CXL 内存)
  • 极冷 → swap 到 NVMe

关键人物

  • Huang Ying:Linux 内核内存 Tiering 的主要开发者,他的方案已经合入主线
  • Al Maruf 的 TPP(Tiered Page Placement) :学术方案,性能不错,但还没进 Linux 主线

3. 加权交错分配(Weighted-interleave)

新分配内存时,不是"全放最快层"(会很快撑爆),而是按权重交错分配

DDR5 : CXL = 70 : 30

意思是:新内存 70% 概率放 DDR5,30% 放 CXL。这样快层不会瞬间满,慢层也能被利用。

内存分层是应对"内存墙"挑战的关键技术之一。其核心思路是构建一个由性能优先的DRAM层(如DDR5) 和成本-容量优化的扩展内存层(如CXL内存) 组成的体系。操作系统会根据数据访问频率,自动将不常访问的"冷数据"迁移到扩展层,从而让宝贵的DRAM资源专注于处理"热数据"。这种策略能在保证绝大多数应用性能的同时,有效降低系统的总体内存成本。

放眼当今2026 年

  1. CXL 3.0 普及:服务器开始插 CXL 内存扩展卡(Samsung CMM-D、Micron CZ120),容量 128GB--1TB/卡,成本约 DDR5 的 40%
  2. AI 工作负载:模型权重几百 GB,不可能全放 HBM/DDR5,必须分层
  3. 云厂商在用:AWS、Azure 的某些实例已经用 CXL 做内存池化

Memory hierarchy - Wikipedia

相关推荐
牧杉-惊蛰23 分钟前
将数组对象根据自定义排列
java·开发语言·算法
砚底藏山河36 分钟前
【量化纯GET实战 #04】批量快照:一次 HTTP GET 取多只股票
java·python·金融·eclipse
君顾138 分钟前
家校托管互通:从需求分析到系统架构的落地实践
java·开发语言·托管
QQ_216962909644 分钟前
【源码编号:project93375】SpringBoot汽车维修管理信息系统:客户车辆、维修预约、工单派发、配件结算全流程实战
java·spring boot·后端·汽车·springboot·需求分析
艾iYYY1 小时前
Linux基础操作指令
linux·运维·服务器
xcl09251 小时前
流浪宠物领养管理系统开发实战:从需求分析到落地的完整指南
java·spring boot·需求分析·宠物
v_for_van1 小时前
C语言__attribute__
服务器·c语言·开发语言·mcu·嵌入式·嵌入式实时数据库
lhldsg1 小时前
家校托管互通系统技术架构与实战设计
java·经验分享·小程序·架构
单线程121381 小时前
从案例分析 Vue3 Tokenizer 源码
前端·javascript·vue.js