存储器 — 金字塔:寄存器、缓存、内存与它们的价格¶
这一章讲什么: 计算机的存储器不是一样「内存」,而是一整座金字塔—— 越快越贵、越小越靠 CPU。本章回答三个问题:这座金字塔为什么能让程序 变快(局部性原理)、塔里的每一层是怎么造出来的(从 D 触发器到 DRAM 电容)、以及「快」和「便宜」这笔账是怎么算的。读完你会理解:为什么 数组比链表快、为什么循环里的代码跑得飞快——这些程序员直觉,底下都是 这座金字塔。
1. 顶层全景:一座五层的金字塔¶
寄存器 ~1 KB 与 CPU 同速 触发器实现 ← 第 7 章的零件
缓存(L1-L3) 64KB/256KB/8MB 慢 10~100 倍 SRAM(触发器阵列)
内存(DRAM) GB 级 慢 ~1000 倍 1 晶体管 + 1 电容
磁盘/SSD 更大 慢得多,块访问 磁性/闪存 ← 本章不讲实现
云/离线 最大 受网络/手动限制 ——
图说:越往上越快越贵越小;程序运行时,数据像水位一样被「逐级复制」往上提:
磁盘 → 内存 → L3 → L2 → L1 → 寄存器。塔的合理性靠两条:局部性 + 逐级摊薄成本。
金字塔的原始动力是一句大实话:「每位计算机用户都想要大容量存储器和 飞快的计算速度。然而,越快的存储器越贵」——鱼和熊掌不可兼得, 于是工程师把两种欲望拆给不同层去满足1。层级结构的根基是局部性: 程序在短时间内倾向于引用附近的内存地址(指令循环、数组顺序访问都是), 所以「把当前用的一小块复制到快层」命中率极高,金字塔就立住了2。
2. 每一层的身份与脾气¶
2.1 大容量存储器:便宜、慢、要「块访问」¶
硬盘、SSD、光盘:非易失(断电不丢)、比 CPU 慢得多、只能以预定大小的块 访问(作者的 Ubuntu 磁盘块是 4 KB)。操作系统配了专门的软件—— 设备驱动程序——替应用程序操作它们(细节超范围,第 20 章讲 I/O 编程)3。
一个影响深远的工作方式:整个程序不会全部载入内存,只有当前处理的部分 才会被操作系统从大容量存储器载入。而且载入是按块搬的——「程序的邻近部分 (指令或数据)很有可能马上就会被用到」,一次搬一整块,下次就不用再碰磁盘了4。 这就是虚拟内存和缓存共同的基本策略:预取一整块,赌它马上被用。
2.2 内存与两种架构:冯·诺依曼瓶颈¶
内存(RAM)通过三根总线与 CPU 通信,「在硬件上与 CPU 同步」—— 程序员指定地址 + 读/写就能访问,不用像 I/O 那样专门编程5。
程序和数据存在同一块内存里,这叫冯·诺依曼架构(von Neumann architecture,1945 年 EDVAC 报告提出)6。它有个结构性缺点:
冯·诺依曼瓶颈:一条指令要读写内存时,在数据传输完成前,
同一条总线没法把下一条指令取进来——取指和数据互抢总线,互相拖慢。
对策:哈佛架构(Harvard architecture)——程序和数据分存两块内存、
各接一条总线,取指和数据访问同时进行。
代价:灵活性下降、内存总量增加、访问硬件翻倍,「这些都提高了成本」。
注意权衡的走向:哈佛架构用成本和灵活性换带宽。现代 CPU 其实是 混合体——L1 缓存分指令/数据两路(哈佛式),更深层级和内存合一(冯·诺依曼式), 兼得两者7。原书还点了冯·诺依曼架构的另一个「缺点」:程序能改写自己 (代码也是数据)——「这通常不是什么好主意」,GNU/Linux 直接禁止程序 自我修改8。第 21 章的特权级会回来管这件事。
2.3 缓存:金字塔的加速器,局部性的收割机¶
观察(作者的):大多数程序占几十到几百 MB,但执行时间大多耗在循环里—— 循环就那几条指令、那几个变量,自身不过数十到几百字节9。于是现代计算机 在内存和 CPU 之间插入高速缓存(cache),「为当前正在处理的程序指令和 变量提供快得多的访问速度」。
作者机器的缓存配置,是本节最具体的一组数字10:
L1:64 KB(哈佛架构:32 KB 指令 + 32 KB 数据),行大小 32 字节(指令)
L2:256 KB 行大小 64 字节(数据)
L3:8 MB 行 64 字节
访问速度:L1 ≈ CPU;L2 慢 ~10 倍;L3 慢 ~100 倍;内存慢 ~1000 倍(近似值)
工作方式(查找链):要访问指令/数据时,先查 L1;不在查 L2;不在查 L3; 不在查内存。每命中下一层,就把包含它的整块——行(line)—— 逐级复制上来(一次复制的行,远小于从大容量存储器复制的块)11。 写入则反向:先写 L1,再层层下写。
主线例子:一次数组求和的旅程。 程序顺序遍历 int 数组(为演示编的数值): 数组基址在内存,循环体在磁盘上的可执行文件里。
第 1 次循环:取指 —— L1 未命中 → L2 未命中 → L3 未命中 → 内存,
把包含这条指令的「行」(32 字节 ≈ 几条指令)搬进 L1/L2/L3;
读数组元素 —— 同样一路未命中,把包含该元素的 64 字节行搬上来。
(这一趟最贵:内存比 L1 慢约 1000 倍。)
第 2…N 次循环:指令在 L1 里 ✓;数组元素若在同一行(64 字节 = 16 个 int),
接着 15 次访问全中 L1 ✓
净效果:付一次「慢 1000 倍」的代价,换 15 次「与 CPU 同速」的访问。
命中率 15/16 ≈ 94%——金字塔的性价比就是这么来的。
图说:这个例子里的数为演示而设;真实的行大小与元素大小见上面的缓存配置。
局部性原理在这里闭环:「缓存的有效性取决于引用的局部性,即程序在短时间内 引用附近内存地址的趋势。这也是优秀的程序员将程序(尤其是重复的部分) 分解成小单元的原因之一」12。写小程序单元、顺序访问数组、让循环体 小巧——这些「好品味」的底层,是 64 字节的行。
2.4 寄存器:塔尖的草稿纸¶
「最快的存储器位于 CPU 内部:寄存器」——约 1 KB 量级,访问速度与 CPU 相同, 用途像手算时的草稿纸:数值计算、逻辑运算、临时存放13。有些寄存器程序员 可直接访问,有些隐藏;x86-64 的 16 个通用寄存器,第 9 章正式介绍。 「CPU 的寄存器组织形式特定于具体的 CPU 架构」——这就是汇编语言 因机器而异的硬件根源。
3. 造一座塔:存储器硬件的实现¶
现在从塔尖往下造。每往下一层,都会撞上一个新的物理/经济约束, 而工程解法就是下一层存在的理由。
3.1 4 位寄存器:触发器 + Store 信号¶
起点:图 8-3,4 个 D 触发器,时钟正沿把输入 d = d₃d₂d₁d₀ 抄进 r。 问题:每个周期都强制写入,寄存器内容只在「一个时钟周期」内有效—— 「但是我们还需要寄存器在被明确改变之前能够将值保存一段时间,这段时间 可能是数十亿个时钟周期」14。
解法:加 Store 信号 + 每位输出 rᵢ 的反馈。规格表(表 8-1)的两行 就是全部要求:Store = 0 时 D = 现态(保持);Store = 1 时 D = dᵢ(写入)。 化简后的布尔式:
D(Store, di, ri) = ¬( ¬(¬Store ∧ ri) ∧ ¬(Store ∧ di) )
用第 5 章的眼睛读:这是一对 NAND 的组合(¬(¬A ∧ ¬B) = A ∨ B):
D = (¬Store ∧ ri) ∨ (Store ∧ di) ← 二选一:Store 选「新值 di」还是「旧值 ri」
图说:一个 2 选 1 复用器(第 6 章 MUX 的与非版),塞在触发器前面。
Store 是选择线,反馈回来的 ri 是「旧值」那一路输入。
「这种设计还具有另一个源于 D 触发器的主/次属性的重要特点」:次级在 时钟后半周期才变,所以前半个周期外部电路可以安心读旧值,主级同时 在准备新值——读写互不干扰,这正是 7.2 节主/次结构的红利第一次变现15。 「我们现在有了一种存储加法器电路运算结果的方法」——寄存器的输出, 可以直接喂给下一级运算电路。
3.2 移位寄存器:串行变并行的翻译官¶
把每个触发器的输出接给序列中下一个触发器的输入(图 8-5),就是 移位寄存器(shift register)16:
串行比特流从 si 进;每个时钟周期,每一位把值传给右边邻居,最左位吃进新 si。
4 位之后:si 处进来的最后 4 位,同时在 r3 r2 r1 r0 上可用(r3 最早)。
→ 串入并出(Serial-In Parallel-Out,SIPO):一根线进来的流,变成 4 根线的并行字。
把 r3 当输出再引一根线 → 这个电路同时是「4 个时钟周期的延迟线」。
图说:数据在触发器里「排队前进」,像传送带;队尾的就是 4 拍前的输入。
应用直觉:串行接口(老式鼠标、某些传感器)一次只来一位;移位寄存器 把「慢慢来的流」攒成「一次能用的字」。第 20 章的 I/O 里全是这种节奏问题。
3.3 寄存器文件:寄存器阵列 + 复用器¶
x86-64 有 16 个 64 位通用寄存器——它们组成一个寄存器文件 (register file)。访问它要「寻址」:小例子用 8 个 4 位寄存器 r0~r7, 3 位选择码17。
读路径:复用器。8 个寄存器的「第 i 位」分别接进一个 8 路复用器的 8 个输入,RegSel 做选择线——读出 r5 的第 i 位。4 位寄存器需要 4 个 这样的复用器,同一个 RegSel 同时驱动,一次读出整个寄存器的 4 位; n 位寄存器就要 n 个18。第 6 章的 MUX 在这里量产了。
写路径:译码器 + Store + 三态缓冲器。写地址经译码器选中唯一一个 寄存器的 Store 端;读路径上,每个寄存器输出配三态缓冲器, 「Read = 0 时输出与后续电路保持断开;Read = 1 时连接输出」19—— 没被点名的寄存器全部高 Z,只有被选中的那一位把数据推上总线。 译码器选谁、复用器读谁、三态缓冲器隔开谁——第 6 章的「会选」三件套, 在寄存器文件里全员到岗。这也预告了第 9 章:CPU 内部的数据通路, 本质就是两个寄存器文件 + 一堆 MUX + 一个 ALU。
3.4 SRAM:大译码器的行列拆分¶
用触发器做的存储器叫 SRAM(静态随机存取存储器): 「静态」= 只要不断电,数据保持不动(不用刷新); 「随机」= 访问任意字节的时间相同20。SRAM 贵(每 bit 约 6 个晶体管), 所以只配做缓存(数 MB 量级)。
造 1 MB 的 SRAM 会撞上一个组合爆炸:寻址 1 MB 需要 20 位地址, 一个 20 × 2²⁰ 的译码器 = 1 048 576 个与门21。这个数字应该让你 倒吸一口气——第 4 章的组合爆炸换了个马甲回来了。工程解法漂亮:
把存储阵列排成 1024 行 × 1024 列的网格;
20 位地址劈成两个 10 位:各驱动一个 10 × 2¹⁰ 译码器(各 1024 个与门),
行译码器选行、列译码器选列,交叉处就是要找的字节。
与门总数:2 × 1024 = 2048 个 —— 是单译码方案的 1/512。
图说:组合爆炸的对策不是硬算,而是拆问题——一行一列两次选择,
恰好把指数劈成两半相乘。(代价:访问逻辑略复杂。)
「这个例子应该能让你体会到工程师是如何简化设计的」——n 位线性译码 需要 2ⁿ 个门,n/2 + n/2 的行列译码只要约 2^(n/2 + 1) 个。DRAM 的 行列地址还因此多赚一笔:地址线可以分时复用(先送行、再送列), 芯片引脚减半——这是第 1 章「总线是逻辑分组」思想的又一次变现 (补充:分时复用为通用知识,书里只给了行列拆分本身)。
3.5 DRAM:一个电容的便宜与一个电容的麻烦¶
SRAM 每位约 6 个晶体管;DRAM(动态随机存取存储器)把一位做成 1 个晶体管 + 1 个电容——电容充到高电压或低电压,就是 1 或 022。
读:行选择 = 1 → 该行所有晶体管导通,电容接入感测放大器/锁存器,
电压被放大并存进锁存器供读取。**但读取本身会让电容放电**——
所以读完必须按锁存器里的值把电容「刷新」回去。
写:新值先存进锁存器,行选择 = 1,感测放大器把对应 0/1 的电压重新充进电容。
维护:电容会漏电——**每行必须每 60 毫秒读一遍再写回去**(刷新电路专职干这个)。
图说:DRAM 的「动态」二字就是这么来的:数据不是「放着不动」的,
是要反复「重描」的——像黑板上的字,每隔一阵就得重写一遍,否则自己淡掉。
后果一条接一条:「这些操作比简单地切换触发器需要更多的时间」——DRAM 比 SRAM 慢得多;刷新电路又叠一层延迟。但便宜是真便宜,所以内存(DRAM)可以 做到 GB 级,缓存(SRAM)只配 MB 级23。金字塔的层间分界,本质是 「每位成本 vs 每位速度」的分界。
4. 作者的判断与证据¶
判断一:局部性是金字塔的地基,也是编程品味的地基。 原书把缓存有效性 直接归因于局部性,并顺势推出「优秀程序员把程序分解成小单元」的编码建议 12——这是全书少数几处把硬件机制直接翻译成软件实践的地方,判断大胆 但证据链完整(行 64 字节 → 循环体越小越装得进行 → 命中率越高)。
判断二:架构之争没有胜负,只有折中。 冯·诺依曼 vs 哈佛:前者灵活省线, 后者带宽翻倍;现代 CPU 混合使用(L1 哈佛、深层冯·诺依曼)7。 和第 4、5、6 章的态度一致:不崇拜任何一种设计,只问「它买的到底是什么」。
判断三:每往下一层,都是一次「用复杂度换规模」的交易。 寄存器加 Store (加 MUX)、寄存器文件加译码/复用(加选择逻辑)、SRAM 译码改行列(加访问 复杂度)、DRAM 加刷新电路(加维护电路)——交易一次比一次大胆,因为规模 一次比一次大。读懂这一串交易,就读懂了存储器工程的全部美学。
5. 边界与局限¶
- 磁盘/SSD 的实现不讨论(自承「大容量存储系统的实现不在本书讨论之列」)24;
- 缓存一致性、写回/写穿、替换策略只一句带过(「有许多缓存使用方案, 这些方案可能会相当复杂」)11——多核时代这些才是主战场;
- 数字(64 KB/256 KB/8 MB、慢 10/100/1000 倍、60 ms 刷新)是作者机器的 实测口径,随机器而变;结构(层级 + 行 + 逐级复制)不变;
- DRAM 的感测放大器、bank、突发传输等现代 DRAM 的核心机制没有展开;
- 没讲虚拟内存(那是操作系统课);金字塔在「内存 ↔ 磁盘」之间还隔着 一层操作系统管理的换页,本书不涉及(补充,不在书里,来自通用知识)。
6. 可带走的¶
全章主线例子,一行走完: 数组求和的第一趟循环(慢 1000 倍的内存访问) 把指令行和数据行搬进缓存,之后 15 次访问全中 L1(与 CPU 同速)—— 一次慢访问摊薄成 16 次快访问,金字塔的性价比全在这笔账上。
- 存储器金字塔:越快越贵越小,层级 = 「快与便宜不可兼得」的工程解;
- 局部性是金字塔的地基:程序短期内反复用附近地址 → 预取整块命中率极高;
- 磁盘按块访问(4 KB),缓存按行访问(32/64 字节)——预取的粒度逐级变细;
- 冯·诺依曼瓶颈 = 取指和数据互抢总线;哈佛架构用成本换带宽;现代 CPU 混合;
- 代码即数据 → 程序能自我修改 → 现代操作系统禁止;
- 缓存查找链 L1→L2→L3→内存,逐级复制「行」;写入反向层层下写;
- 寄存器 ≈ 1 KB 草稿纸;「寄存器组织因架构而异」是汇编语言的存在理由;
- 寄存器 = D 触发器 + Store(MUX 选新值/旧值);主/次结构让前半个周期 可读旧值、后半写新值;
- 移位寄存器:串入并出 / 延迟线,触发器排队前进;
- 寄存器文件 = 寄存器阵列 + 译码器(写)+ 复用器(读)+ 三态缓冲器(隔离);
- 20 位单译码 = 100 万与门;行列两译码 = 2048 个——组合爆炸的 标准拆法:把指数劈成两半;
- SRAM = 触发器(静态、6 管/位、贵、快)→ 缓存;DRAM = 晶体管+电容 (动态、1 管 1 容/位、便宜、慢、要每 60 ms 刷新)→ 内存;
- 编程品味有硬件账单:小循环体、顺序访问、小数据单元 = 高缓存命中率。
7. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 「越快越贵」的金字塔动力 | 第8 章 存储器 | text/14-ch08.txt:11(搜「越快的存储器越贵」) |
| 大容量存储器与 4 KB 块 | 第8 章 存储器 | text/14-ch08.txt:43(搜「大容量存储器」) · text/14-ch08.txt:64(搜「4 KB」) |
| 按需载入与整块预取 | 第8 章 存储器 | text/14-ch08.txt:62(搜「不会全部加载」) |
| 冯·诺依曼架构与瓶颈 | 第8 章 存储器 | text/14-ch08.txt:70(搜「冯·诺依曼架构」) · text/14-ch08.txt:78(搜「瓶颈」) |
| 哈佛架构与自我修改禁令 | 第8 章 存储器 | text/14-ch08.txt:80(搜「哈佛架构」) · text/14-ch08.txt:85(搜「禁止」) |
| 循环只占几十字节的观察 | 第8 章 存储器 | text/14-ch08.txt:89(搜「循环」) |
| 三级缓存配置与行大小 | 第8 章 存储器 | text/14-ch08.txt:93(搜「64 KB」) · text/14-ch08.txt:105(搜「32 字节」) |
| 查找链与逐级复制 | 第8 章 存储器 | text/14-ch08.txt:95(搜「一级缓存」) · text/14-ch08.txt:106(搜「写入」) |
| 慢 10/100/1000 倍 | 第8 章 存储器 | text/14-ch08.txt:108(搜「慢 10 倍」) |
| 局部性与小程序单元 | 第8 章 存储器 | text/14-ch08.txt:116(搜「局部性」) |
| 寄存器 = 草稿纸 | 第8 章 存储器 | text/14-ch08.txt:130(搜「草稿纸」) |
| 4 位寄存器与「数十亿个时钟周期」 | 第8 章 存储器 | text/14-ch08.txt:144(搜「4 位寄存器」) · text/14-ch08.txt:291(搜「数十亿」) |
| Store 信号与布尔等式 | 第8 章 存储器 | text/14-ch08.txt:156(搜「Store」) · text/14-ch08.txt:170(搜「D(Store」) |
| 主/次属性的读写红利 | 第8 章 存储器 | text/14-ch08.txt:173(搜「前半个时钟周期」) |
| 移位寄存器(SIPO) | 第8 章 存储器 | text/14-ch08.txt:187(搜「串入并出」) |
| 寄存器文件与 8 路复用器 | 第8 章 存储器 | text/14-ch08.txt:201(搜「寄存器文件」) · text/14-ch08.txt:208(搜「复用器」) |
| 读-写寄存器与三态缓冲 | 第8 章 存储器 | text/14-ch08.txt:216(搜「读-写」) · text/14-ch08.txt:219(搜「断开」) |
| SRAM 定义 | 第8 章 存储器 | text/14-ch08.txt:231(搜「静态随机存取存储器」) |
| 20×2²⁰ 译码器 = 百万与门 | 第8 章 存储器 | text/14-ch08.txt:238(搜「20 × 220」) · text/14-ch08.txt:242(搜「1 048 576」) |
| 行列拆分 = 2048 个与门 | 第8 章 存储器 | text/14-ch08.txt:243(搜「网格」) · text/14-ch08.txt:245(搜「2048」) |
| DRAM:1 管 1 容、读放电、60 ms 刷新 | 第8 章 存储器 | text/14-ch08.txt:256(搜「6 个晶体管」) · text/14-ch08.txt:264(搜「放电」) · text/14-ch08.txt:271(搜「60 毫秒」) |
| 层级结构与位成本结论 | 第8 章 存储器 | text/14-ch08.txt:274(搜「位成本」) |
| 小结:四层各自实现 | 第8 章 存储器 | text/14-ch08.txt:283(搜「存储器层级结构」) |
-
出处:「第8 章 存储器」第 11 段(
text/14-ch08.txt:11,搜「越快的存储器越贵」)。原文:「每位计算机用户都想要大容量存储器和飞快的计算速度。然而,越快的存储器越贵,所以存在一些权衡。」 ↩ -
出处:「第8 章 存储器」第 116 段(
text/14-ch08.txt:116,搜「局部性」)与第 116 段(text/14-ch08.txt:116,搜「局部性」)。原文:「缓存的有效性取决于引用的局部性,即程序在短时间内引用附近内存地址的趋势。」 ↩ -
出处:「第8 章 存储器」第 43 段(
text/14-ch08.txt:43,搜「大容量存储器」)与第 34 段(text/14-ch08.txt:34,搜「远离 CPU 的下一层代」)。原文:「大容量存储器(mass storage)用于以机器可读的格式保存程序和大量的数据……其存储的内容是非易失性的」「操作系统提供了可供应用程序调用的软件(设备驱动程序),能够通过 SATA 端口访问 SSD 卡中的数据和应用程序。」 ↩ -
出处:「第8 章 存储器」第 62 段(
text/14-ch08.txt:62,搜「不会全部加载」)与第 64 段(text/14-ch08.txt:64,搜「4 KB」)。原文:「整个程序和数据集通常不会全部加载到内存中。只有当前正在处理的部分才会被操作系统从大容量存储器载入内存」「现代计算机中的大多数大容量存储设备只能以预定大小的块来访问。例如,我的 Ubuntu 系统的磁盘块大小为 4 KB。」 ↩ -
出处:「第8 章 存储器」第 59 段(
text/14-ch08.txt:59,搜「RAM」)。原文:「内存通过数据总线、地址总线、控制总线与 CPU 通信……内存与 CPU 同步。因此,程序员可以通过简单地指定地址以及读或写操作来访问内存中的数据。」 ↩ -
出处:「第8 章 存储器」第 70 段(
text/14-ch08.txt:70,搜「冯·诺依曼架构」)。原文:「内存最常见的组织形式是将程序指令和数据都存储在其中。这称为冯·诺依曼架构(von Neumann architecture),是由 John von Neumann 提出的(“First Draft of a Report on the EDVAC”, Moore School of Electrical Engineering, University of Pennsylvania, 1945)」。 ↩ -
出处:「第8 章 存储器」第 80 段(
text/14-ch08.txt:80,搜「哈佛架构」)与第 103 段(text/14-ch08.txt:103,搜「一级缓存通常采用哈佛架构」)。原文:「哈佛架构(Harvard architecture),在这种架构中,程序和数据分别存储在不同的内存中,各自都有自己的总线连接到 CPU」「一级缓存通常采用哈佛架构,从而为指令和数据提供了独立的 CPU 路径。」 ↩↩ -
出处:「第8 章 存储器」第 84 段(
text/14-ch08.txt:84,搜「自我修改」)。原文:「程序可以将自身视为数据进行改写,从而实现自我修改的程序,这通常不是什么好主意。像大多数现代通用操作系统一样,GNU/Linux 禁止程序自我修改。」 ↩ -
出处:「第8 章 存储器」第 89 段(
text/14-ch08.txt:89,搜「循环」)。原文:「我使用的大多数程序都占用几十或几百 MB 的内存。但是大部分执行时间都被循环占用了,循环重复执行同样的几条指令,访问相同的几个变量,自身不过数十或几百字节。」 ↩ -
出处:「第8 章 存储器」第 93 段(
text/14-ch08.txt:93,搜「64 KB」)。原文:「我使用的计算机共配备了三级缓存:64 KB 的一级缓存、256 KB 的二级缓存和 8 MB 的三级缓存。」 ↩ -
出处:「第8 章 存储器」第 97 段(
text/14-ch08.txt:97,搜「行」)与第 106 段(text/14-ch08.txt:106,搜「写入」)。原文:「一次性复制到缓存中的内存量称为行(line),远小于从大容量存储器中复制的数量」「当向内存写入数据时,先从一级缓存开始,然后是下一级缓存,最后是内存。有许多缓存使用方案,这些方案可能会相当复杂。」 ↩↩ -
出处:「第8 章 存储器」第 116 段(
text/14-ch08.txt:116,搜「局部性」)。原文:「缓存的有效性取决于引用的局部性,即程序在短时间内引用附近内存地址的趋势。这也是优秀的程序员将程序(尤其是重复的部分)分解成小单元的原因之一。小的程序单元更有可能放进若干行缓存,便于随后的重复访问。」 ↩↩ -
出处:「第8 章 存储器」第 130 段(
text/14-ch08.txt:130,搜「草稿纸」)。原文:「最快的存储器位于 CPU 内部:寄存器。寄存器通常提供 1 KB 左右的存储空间,访问速度与 CPU 相同。寄存器主要用于数值计算、逻辑运算、临时数据存储和类似的短期操作——有点像我们使用草稿纸进行手动计算。」 ↩ -
出处:「第8 章 存储器」第 149 段(
text/14-ch08.txt:149,搜「一个时钟周期」)与第 291 段(text/14-ch08.txt:291,搜「数十亿」)。原文:「在下一个时钟周期,di 的任何变化都会改变相应存储位 ri 的状态,因此寄存器的内容实际上仅在一个时钟周期内有效」「但是我们还需要寄存器在被明确改变之前能够将值保存一段时间,这段时间可能是数十亿个时钟周期。」 ↩ -
出处:「第8 章 存储器」第 173 段(
text/14-ch08.txt:173,搜「前半个时钟周期」)。原文:「这种设计还具有另一个源于 D 触发器的主/次属性的重要特点。次级部分的状态直到后半个时钟周期才会改变。因此,连接到该寄存器输出端的电路可以在前半个时钟周期读取当前状态,而主级部分则准备有可能要将状态更改为新内容。」 ↩ -
出处:「第8 章 存储器」第 187 段(
text/14-ch08.txt:187,搜「串入并出」)。原文:「我们可以将移位寄存器作为一种串入并出(Serial-In Parallel-Out,SIPO)设备。移位寄存器使用一系列 D 触发器……但是每个触发器的输出连接到序列中下一个触发器的输入。」 ↩ -
出处:「第8 章 存储器」第 201 段(
text/14-ch08.txt:201,搜「寄存器文件」)。原文:「CPU 中用于类似操作的寄存器被组合为一个寄存器文件(register file)。例如,正如你将在第 9 章中看到的,x86-64 架构包括 16 个 64 位通用寄存器。」 ↩ -
出处:「第8 章 存储器」第 208 段(
text/14-ch08.txt:208,搜「复用器」)。原文:「我们可以将一个 3 × 8 复用器连接到 8 个寄存器的各个对应位……一个 4 位寄存器需要 4 个这样的复用器输出电路。」 ↩ -
出处:「第8 章 存储器」第 219 段(
text/14-ch08.txt:219,搜「三态缓冲器」)与第 219 段(text/14-ch08.txt:219,搜「断开」)。原文:「三态缓冲器可以做到这一点……该电路仅用于单个 4 位寄存器」「当 Read = 0 时,4 位输出 r3r2r1r0 与 D 触发器保持断开。Read = 1 时连接输出。」 ↩ -
出处:「第8 章 存储器」第 231 段(
text/14-ch08.txt:231,搜「静态随机存取存储器」)。原文:「我们一直在讨论的使用触发器的存储器称为静态随机存取存储器(Static Random Access Memory,SRAM)。之所以称之为“静态”,是因为只要不断电,存储的数据就保持不变。所谓的“随机”,是指访问存储器内的任意(随机)字节所使用的时间都是一样的。」 ↩ -
出处:「第8 章 存储器」第 242 段(
text/14-ch08.txt:242,搜「1 048 576」)。原文:「n × 2n 译码器需要 2n 个与门。所以一个 20 × 220 译码器需要 1 048 576 个与门。」 ↩ -
出处:「第8 章 存储器」第 256 段(
text/14-ch08.txt:256,搜「6 个晶体管」)至第 259 段(text/14-ch08.txt:259,搜「晶体管给电容充电」)。原文:「SRAM 中的每一位大约需要 6 个晶体管来实现」「DRAM 中的一位通常通过将电容器充电到两种电压之一来实现。这种电路只需要一个晶体管给电容充电。」 ↩ -
出处:「第8 章 存储器」第 271 段(
text/14-ch08.txt:271,搜「慢得多」)与第 271 段(text/14-ch08.txt:271,搜「60 毫秒」)。原文:「这些操作比简单地切换触发器需要更多的时间,所以 DRAM 比 SRAM 要慢得多。此外,电容器会随着时间的推移而掉电,因此每行电容器必须每 60 毫秒读取和刷新一次。」 ↩ -
出处:「第8 章 存储器」第 137 段(
text/14-ch08.txt:137,搜「不在本书讨论」)。原文:「大容量存储系统的实现不在本书讨论之列。」 ↩