Lec 10高级访存操作(Advanced Memory Operations)
Lec 10 高级访存操作(Advanced Memory Operations)
MIT 6.5900 Fall 2024 · Joel Emer 主题:写性能与流水化写、写策略、读缺失代价、推测 load/store 与存储缓冲、内存依赖预测(Store Sets)、预取
TL;DR
- 写流水化与策略:针对写操作 Tag 检查与数据写入天然串行的痛点,采用延迟写缓冲(Delayed Write Buffer)与旁路技术,并结合写回与写分配策略最小化访存流量。
- 推测存储与地址消歧:乱序核中 Store 写入被惰性缓存在 Store Buffer 中直至退休提交;Load 则通过 Store-to-Load 旁路或激进推测执行,违例时由 Load Queue 侦测并触发回滚。
- 依赖预测与硬件预取:借助 Store Sets 预测器精准消除假内存依赖停顿;多流步长预取器则通过前瞻加载掩盖强制与容量缺失延迟。
一、写性能
直接映射缓存(Direct-Mapped Cache)的读:tag 检查与数据读可并行。但写呢?
写在访存级要两个周期:一个周期做 tag 检查,命中后再一个周期写数据——完全串行!
降低写命中时间
把"命中/缺失"视为对微架构值的数据依赖。解法:
- 停顿——尽快交付数据:电路级技术(CAM tag)或许能一周期写;
- 推测命中 + 急切更新数据:设计能在一周期内同时读写的数据 RAM,tag 缺失(abort)后恢复旧值;
- 推测缺失 + 惰性更新数据:把 store 写数据暂存在缓存前的单个缓冲,在下一个空闲数据访问周期写入缓存(commit)。
流水化/延迟写
问题:需要提交惰性保存的写数据。解法:在下一个 store 做 tag 检查时的空闲数据周期写入数据。
流水化写:若指令需要延迟写缓冲中的数据怎么办?→ 旁路。旁路条件:tag 与 index 都匹配"延迟写地址"。
二、写策略选择
命中时:
- 写穿透(Write-through):同时写缓存与内存——流量大但简化多处理器设计;
- 写回(Write-back):只写缓存(逐出时才写内存)——每块一个脏位可进一步减流量。
缺失时:
- 不写分配(No-write-allocate):只写主存;
- 写分配(Write-allocate / fetch on write):取入缓存。
常见组合:写穿透 + 不写分配、写回 + 写分配。
降低读缺失代价
问题:写缓冲可能持有读缺失所需位置的更新值(RAW 数据冒险)。
- 停顿:读缺失时等写缓冲排空;
- 旁路:把写缓冲地址与读缺失地址比较,无匹配则让读缺失先行,有匹配则返回写缓冲中的值。
写缓冲持有:写回缓存的逐出脏行,或写穿透缓存的所有写。
三、推测 load / store
寄存器依赖已用 tag/物理寄存器号处理,那么内存操作呢?
问题:与寄存器更新一样,store 在指令提交前不应永久改变体系结构内存状态。
- 数据更新策略:急切还是惰性?→ 惰性:加推测存储缓冲(speculative store buffer)惰性持有推测 store 数据;
- store-to-load 数据冒险:停顿/旁路/推测?→ 旁路。
存储缓冲的职责
- 惰性存数据:缓冲 store 的新数据值;
- 提交/中止:最老指令的数据须提交到内存或被遗忘;
- 旁路:在较老 store 提交前,把其数据转发给较年轻的指令。
提交一般按序进行——为什么?WAW 冒险。
存储缓冲——惰性数据管理
- store 执行时:标记 valid + speculative,存 tag、data、指令号;
- store 提交时:清 speculative 位,最终把数据移入缓存;
- store 中止时:清 valid 位。
存储缓冲——旁路
- 数据同时在存储缓冲与缓存中用哪个?→ 存储缓冲(若 store 比 load 老);
- 同地址在存储缓冲出现两次用哪个?→ 比 load 老的最年轻 store;
- 旁路需检查的字段:Valid、Inum、tag;
- 计算所需存储缓冲项可视为对索引的依赖,可用简单预测器推测;猜错则声明误推测并中止。
四、内存依赖
对寄存器用 tag/物理寄存器号判依赖,内存操作呢?
sw x1, (x2)
lw x3, (x4)
load 何时依赖 store?——当 x2 == x4 时。ROB 在发射时知道吗?→ 不知道。
1. 按序内存队列(朴素停顿)
按程序序执行所有 load/store ⇒ load/store 须等之前所有 load/store 执行完才能开始(仍可相对其他指令推测、乱序执行)。
2. 保守乱序 load 执行(智能停顿)
把 store 拆成两阶段:地址计算与数据写。
- 若地址已知且
x4 != x2,可在 store 前执行 load; - 每个 load 地址与之前所有未提交 store 的地址比较(可用部分保守检查,如地址低 12 位);
- 若有任何之前 store 地址未知,则不执行该 load(MIPS R10K,16 项地址队列)。
3. 地址推测(Address Speculation)
- 猜
x4 != x2,在 store 地址已知前执行 load; - 若确实
x4 != x2则提交; - 若
x4 == x2则压制(squash)load 及其后所有指令。
为支持 squash,需按程序序保留所有已完成但未提交的 load/store 地址与数据。如何检测需 squash?——监视在需要其数据的 load 之后到达的 store。
推测 load 缓冲(Speculative Load Buffer)
- load 执行时:标记 valid,设指令号与地址 tag;提交/中止时清 valid;
- 推测检查:检测某 load 是否在对同地址的较早 store 之前执行了(漏掉的 RAW 冒险);
- store 地址到来时,若 load 缓冲中有指令号比该 store 年轻的项 → 推测违例,中止!
tag 匹配不必完美(部分匹配即可);不准确的地址推测代价大 → 用预测降低误推测几率。
五、内存依赖预测
简单方案(Alpha 21264)
- 猜
x4 != x2,在 store 前执行 load; - 若后来发现
x4 == x2,squash load 及其后指令,并把该 load 标为 store-wait; - 该 load 指令的后续执行将等待所有之前 store 完成;周期性清除 store-wait 位。
注意预测器的通病:学到了却难以"忘掉"。
Store Sets(Alpha 21464)
针对多读者/多写者(多代码路径、单一位置的多个组成部分):
- 每个 load 必须等待其 store set 中尚未执行的 store;
- 处理器先允许朴素推测、记录内存序违例,从而近似每个 load 的 store set;
- Store Set Map Table:用 store/load 索引把引起内存序违例的 store/load 对关联起来,并支持多读者共享同一 store set。
六、预取(Prefetching)
load 的执行"依赖"其所需数据在缓存中——可推测未来的指令与数据访问并预取入缓存(指令访问比数据访问更易预测)。
种类:硬件预取、软件预取、混合方案。
预取对缺失的影响:强制缺失减少,冲突/容量缺失约略增加。
预取的三个要点
- 有用性:应产生命中;
- 及时性:不太晚也不太早;
- 缓存与带宽污染。
硬件指令预取(Alpha 21064)
缺失时取两块:请求块 放入缓存,下一块 放入指令流缓冲(stream buffer);若缓存缺失但流缓冲命中,则把流缓冲块移入缓存并预取 。
硬件数据预取
- 缺失时预取:缺失块 时预取 ;
- 单块前瞻(OBL):访问块 时为 发起预取(与"加倍块大小"不同);可扩展为 N 块前瞻(流预取);
- 步长预取(Strided):若观察到访问序列 ,则预取 等。
例:IBM Power 5(2003)每处理器支持 8 个独立步长预取流,预取到当前访问之前 12 行。
高级访存与内存依赖预测总结
- 写比读慢(tag 检查 + 数据写串行),用 CAM tag、推测命中急切更新或延迟写缓冲 + 旁路优化;写策略在流量与复杂度间权衡;
- 推测 load/store 用惰性存储缓冲保存推测数据,store-to-load 用旁路;提交按序以避 WAW;
- 内存依赖在发射时未知,用按序队列 → 保守乱序 → 地址推测 + 推测 load 缓冲逐步放开,并用 store-wait / Store Sets 预测内存依赖;
- 预取(硬件/软件)减少强制缺失,关键在有用性、及时性与污染控制。
内存消歧(Memory Disambiguation)与动态数据流推测
与寄存器依赖在译码阶段即可通过寄存器名字静态确定不同,内存访问的地址只有在执行阶段由 ALU 计算出有效地址(Effective Address)后才得以揭晓。这使得内存维度的 RAW、WAR 和 WAW 依赖成为乱序处理器中最棘手的性能瓶颈——如果让每一个 Load 严格等待前方所有未知地址的 Store 算完地址,指令级并行度将被严重扼杀;而若盲目推测 Load 与前方 Store 绝不重叠(地址推测),一旦发生别名冲突(Aliasing),流水线冲刷的代价又极为高昂。
现代处理器的破局之道是建立「推测-监听-预测」闭环:Load Queue(推测 Load 缓冲)记录在飞 Load 的地址与时间戳;当迟到的 Store 完成地址计算时,反向侦听 Load Queue 捕捉违例;同时,诸如 Store Sets 等依赖预测器动态学习特定 PC 间的真依赖模式。通过只让高概率冲突的访存指令等待、放行绝大多数无关 Load,处理器成功将原本高开销的保守屏障转化为高效的推测数据流旁路。