SIGNAL ONLINE 缓存与局部性 CACHE & LOCALITY // 复用即加速

缓存与局部性

CACHE & THE LOCALITY PRINCIPLE // 快的东西,都是被复用的东西

> CPU 比内存快 100 倍,内存比磁盘快 10 万倍——计算机却没有瘫痪,靠的是一个赌注:程序会反复访问刚访问过的东西(时间局部性),以及它旁边的东西(空间局部性)。缓存就是把赌注变成硬件:小而快的层,存放"可能马上再用"的少数。整个存储金字塔、CDN、Redis、浏览器缓存、CPU L1/L2/L3,全是同一条定律的化身:复用即加速

SUBJECT: 计算机体系结构 FILE: cards/cache-locality SINCE: 1965–68 BUILD v1.0

Principle — 原理与来源

局部性原理 LOCALITY PRINCIPLE // Denning 1968
刚访问的,很快会再访问;刚访问的旁边,也快被访问
AMAT = Hit + MissRate × Penalty
Average Memory Access Time = 命中时间 + 未命中率 × 未命中代价

理论奠基:彼得·丹宁(Peter J. Denning)1968 年在虚拟内存研究中提出工作集模型(working set)并系统化"访问局部性"原理——程序在任意时刻真正活跃的页面只有一小簇(工作集),且簇随时间缓慢漂移。更早的思想源头:Wilkes 1965提出带"slave memory"的层次存储设想;1968 年 IBM System 360/85首次商用缓存并把 "slave memory" 更名为 cache(法语"隐藏的储藏处")。Denning 后来说:局部性原理"从虚拟内存的挣扎中诞生,最终统治了计算的一切层"。

两类局部性:时间局部性——刚被引用的信息很快再被引用(循环变量、热点函数、最近打开的文件);② 空间局部性——刚被引用的邻近信息很快被引用(数组顺序遍历、指令顺序执行)。衍生的工程手段:预取(prefetch,赌空间)、替换策略(LRU/FIFO,赌时间)。缓存的一切设计,都是对这两句话下注

核心公式(AMAT):平均访问时间 = 命中时间 + 未命中率 × 未命中代价。命中 1ns、未命中 100ns 时:90% 命中 → 1+0.1×100=11ns;99% 命中 → 2ns;99.9% → 1.1ns命中率每多一个 9,性能上一个数量级——这就是为什么缓存工程师为一个百分点拼命。

无处不在的分层:寄存器 → L1/L2/L3 → 内存 → SSD → 磁盘 → 网络(CDN/Redis/浏览器缓存);甚至 TLB 缓存地址翻译、branch predictor 缓存分支历史、DNS 缓存、你桌上的常用文件夹——只要存在"访问不均 + 重访概率 + 层间速度差",缓存就会出现。它是帕累托法则的硬件化:少数数据承载多数访问。

正名:"缓存定律"不是标准术语——规范概念是局部性原理(principle of locality / locality of reference)与缓存设计准则;中文语境的"缓存三定律"多为培训机构总结,非公认命名。引用时用"局部性原理",出处可追 Denning 1968。 ② cache ≠ 缓存 Law 的"发明者一人论"——层次存储思想 Wilkes 1965 已有,Denning 1968 给的是程序行为侧的理论(工作集/局部性),IBM 360/85 是工程首发:三线并行,不是单人成就。 ③ 局部性是赌注不是保证——随机访问模式下命中率趋近于零,缓存只剩纯开销;linked list 按指针跳转 vs 数组顺序扫描的性能鸿沟(可达 10 倍+)就是空间局部性的存在性证明。 ④ 缓存一致性与缓存穿透/击穿/雪崩是工程问题不是原理——多核 MESI 协议、Redis 三件套(穿透=查不存在的 key、击穿=热 key 失效、雪崩=同时失效)是原理落地后的衍生战场,别混进原理表述。 ⑤ "缓存友好"代码是可量化的——行遍历 vs 列遍历二维数组、结构体拆分(AoS→SoA)、循环顺序调整,都是重排访问模式以提高局部性;"高级语言不用管缓存"在性能敏感场景已被证伪(cache miss 可占运行时间 50%+)。 ⑥ Denning 的谦辞——他强调局部性是"经验规律"(empirical regularity):几乎一切真实程序都展现它,但没有定理保证;恶意构造的程序(随机跳转)可以击穿它。称"定律"时记得这个边界。

Apply — 用在哪里

代码性能行优先遍历、数组替代链表、热数据打包(AoS→SoA)——同样算法复杂度下,仅靠局部性就能快数倍;性能优化先看访问模式再看算法。
架构设计Redis/本地缓存/CDN 的本质是在速度断层间插入赌局:先问"重访率够高吗、失效代价可控吗"——帕累托给你命中率上限,AMAT 告诉你值不值。
缓存策略TTL/容量/淘汰策略(LRU 赌时间局部性、prefetch 赌空间局部性);警惕三件套:穿透用空值缓存/布隆过滤器、击穿用互斥重建、雪崩用 TTL 加随机抖动。
数据库索引就是磁盘世界的缓存地图;热点页/缓冲池命中率直接决定 QPS——慢查询优化的第一问常常不是"执行计划",而是"缓冲池为什么没接住"。
生活迁移常用药放床头(L1)、工具箱挂墙上(L2)、仓库(L3)——你的桌面就是 L1 cache;整理术的本质是提升"常用物品的命中率"。

Simulate — 命中率计算器 × 访问模式实验

双视角实验室 // 一个 9 值多少钱;局部性值多少命中率
视角 A:AMAT 计算器——命中率与代价如何合成平均延迟;视角 B:三种访问模式跑 LRU 缓存,看命中率天壤之别
AMAT(平均访问时间) 命中耗时基线 ▲ 纯内存基线(无缓存直访)

Personal Takeaways — 个人启示 · 03

01

速度来自复用,不来自更快

存储金字塔每一层都没有"消灭"慢层,只是在它前面加了一个赌局。性能工程的本质不是买更快的硬件,是提高重访率——你的代码、架构、工作流同理:先问"什么在被反复计算/反复获取",答案就是你的缓存候选。

02

命中率是乘法世界的杠杆

AMAT 公式的残酷在于未命中率乘以惩罚:惩罚越大(网络 vs 内存、磁盘 vs 内存),命中率的每一个 9 越值钱。先看访问断层有多大,再决定为命中率投入多少——在断层小的地方死磕缓存,是优化的常见错位。

03

整理你的人生缓存层级

局部性原理对人也成立:常用物上手边、常访信息进收藏、常走路线做成例程。时间管理的秘诀不在"更快地找",而在"把高频搬近"——每周回顾你的"访问日志",重新摆放你的 L1。