1. 加快内存访问执行速度
1. 一旦 load/store 的目标地址已知,就发起预取数据(prefetch)请求;
2. 一旦 load/store 的目标地址能预测地址,就提前预取数据;
然后 load/store 就 来 操作cache(而不是内存) , 从而减少延迟
2. 允许多个访问指令(S->L L->L)重叠进行 , 即 允许此种情况下的 后面的 load 提前投机执行
但这样的重叠可能破坏访问的原子性,因此需要安全机制来检测出原子性被破坏时,进行恢复/补救。
1. 需要跟踪原子性
2. 原子性被破坏的意思是:原子性被破坏 = 执行顺序造成了程序“看见了不应该看到的值”
3. 如何恢复:
执行顺序可以乱,但提交顺序不能乱,从而仍然保持 S->L L->L
L/S->L :
将 前面的指令(L/S) 称为 A , 后面的 L 为 B
当 A 被执行时,此时 B 已经投机执行过.
如果 在 A 执行 过程中, 投机执行过的B 所涉及的块 会被从高速窃取或替换 (不一定是因为A)
则 B 必须 重新 执行(即在A执行后重新执行,此时就不是投机执行了)
B 投机访问了它要读的 cache block,读取到了值。
后面,A 被执行。
执行 A 的过程中,发现:
为了保持 SC 语义(必须让执行结果就像所有处理器在某个全局顺序下运行),必须:
这是个非常好的问题。我们现在专门来分析:在 L→L(Load followed by Load) 的情形下,是否可能发生第一次投机执行的 B(Load)读取的值和重新执行 B 时的值不一样 —— 答案是:是的,完全可能发生。
你关心的是下面两个条件能否同时满足:
我们马上给出一个完整的例子来说明这种情况真实可发生:
两个处理器:P1 和 P2
初始内存值:
A = 0
B = 0
r1 = *A; // L1:先读 A
r2 = *B; // L2:后读 B(被提前投机执行)
*B = 1; // 写 B(可能引发 P1 的 B block invalidation)
*A = 1; // 写 A(此写影响 P1 的 L1)
B = 0
,所以 r2 = 0
*B = 1
:这个写操作对 P1 的 cache 发送了 invalidate
,B 的 cache block 被踢掉*A = 1
1
r2 = 1
在 L→L 的场景中,确实存在 第二个 Load(B)投机执行获得的值,后来在第一个 Load(A)执行后检测失效、重执行并获得不同值 的真实可能性。
这种现象解释了为什么必须在投机执行后做原子性校验和 rollback,否则就可能违反 顺序一致性(SC) 的语义。
需要我把这个例子画成时序图吗?可以让流程更清晰。
TODO