沿访存依赖路线,把最近写转发、违例恢复与Store Set预测接成一个可逐步核对的窗口。你要交出的不是“乱序执行更快”这句话,而是哪次load读了谁、错误传播到哪里、恢复后哪些身份还有效,以及哪些值最终通过了退休边界。
下载完整Python标准库参考器和全部结果与事件日志。需要Python 3.10或更高版本;运行 python foundation-memory-dependence-check.py,再运行 python -O foundation-memory-dependence-check.py,两份标准输出应完全一致。程序自身不读写外部数据文件,若需保存结果,可由运行者重定向标准输出。下载的JSON是完整运行产物,不是为了页面另写的一套预期值。
一、先把输入和动作的边界写清
程序是有限的 Op 列表,种类为L或S。PC是非负、四字节对齐的整数,同一个PC可以多次出现,但不能一会儿表示load、一会儿表示store。内存地址是任意整数槽名,缺省值0;它与PC的对齐规则不同,不模拟字节访问。值用数学整数,不截断为32位。布尔值不能当PC、槽名、数值、引用编号或延迟使用。
地址或store数据可以是常量,也可以是 Ref(j,a,b),表示更老load j的结果乘a再加b。引用必须指向确实存在且更老的load,不能引用年轻项、自己或store。load没有数据操作数,store必须有。每个字段的驱动释放延迟是正整数。
提交接口表,至少包含这些动作:
| 接口 | 成功时的变化 | 尚不能执行时 |
|---|---|---|
address(token) |
计算该项地址;store地址首次揭晓还可能触发REPLAY | 源load尚无值则WAIT |
data(token) |
计算store数据 | 源load尚无值则WAIT |
finish_store(token) |
地址、数据和预测前驱齐备后标DONE | 字段或前驱未好则WAIT |
load(token) |
真实来源查询成功后记录地址、来源、结果并标DONE | 地址、前驱或来源未好则WAIT |
retire() |
只提交DONE队首;store写M,load记录提交值 | 队首未好或窗口已空则WAIT |
clear_history() |
清历史并重建当前预测前驱 | 不清M,不重置已计算字段 |
手动调用表达的是一个合法事件次序,字段延迟只约束自动驱动器何时尝试该动作。手动样例日志的round为0,不要把它解释成这些操作都在一个硬件周期里完成。自动驱动每轮依次进行一次队首退休、地址阶段、数据阶段、store完成阶段、至多一次成功load;刚产生的load值到下一轮才能进入地址/数据阶段。
保存一个当前token,再制造后缀恢复。用旧token、另一个窗口的token,以及新造但字段相同的 Token 分别尝试动作;三者都应在改状态之前拒绝。另测重复地址动作、把load交给store数据接口、布尔编号,并比较拒绝前后的 state() 和事件列表。空程序应在0轮结束、没有事件。轮数预算耗尽应抛异常,不能当作成功的半份输出。
二、最近同址写还没数据时,不能找别人替它
令当前load读槽4,M[4]=3。输入每行是 (store年龄,地址或None,数据或None);调用者应只传入这条load之前、尚未退休的store。先手算下面五项,再核对JSON中的 examples.source_queries。
| 较老store行 | 保守模式 | 期望结果 |
|---|---|---|
(0,None,17),(2,4,23) |
是 | WAIT,未知地址年龄0 |
| 同上 | 否 | VALUE,来源2,值23 |
(0,4,17),(2,4,None) |
是 | WAIT,匹配数据年龄2 |
(0,4,17),(2,4,23) |
是 | VALUE,来源2,值23 |
(0,9,None) |
是 | VALUE,来源−1,值3 |
第三项不能返回17,也不能返回3。第五项不能因为无关store的数据没到而等待。第二项允许忽略未知地址,但它只给出这一次来源查询的许可,不独自保证整个程序安全;迟到地址仍要经过下一项的检查。
把行顺序打乱,保留年龄字段,答案应不变。再把年龄2的数据由None改成23,说明为什么解除的是匹配数据等待,而不是未知地址等待。提交最大年龄选择的证明,并给出一个把 max 错写为 min 时可见的反例。
三、先算出7,为什么最终只能提交35
使用四条程序,初始M[4]=3、M[8]=0:
I0: store [4] := 17 PC 0x100,地址释放延迟6
I1: v1 := load [4] PC 0x104
I2: store [8] := 2*v1+1 PC 0x108
I3: v3 := load [8] PC 0x10c
其余延迟均为1。分别新建保守、盲猜和空历史预测窗口。每份账本保留load执行时的代次、地址、来源和数值,重放取消列表,以及每次退休后的架构M和已提交load表。
| 模式 | 第一次load执行 | 违例及取消 | 实际load执行数 | 退休轮数I0/I1/I2/I3 |
|---|---|---|---|---|
| 保守 | I1第6轮从0得17,I3第7轮从2得35 | 无 | 2 | 7 / 8 / 9 / 10 |
| 盲猜 | I1第1轮从M得3,I3第2轮从2得7 | 第6轮取消I1–I3 | 4 | 7 / 8 / 9 / 10 |
| 空历史预测 | 与盲猜相同 | 同时学习0x100与0x104 | 4 | 7 / 8 / 9 / 10 |
盲猜的I0地址揭晓动作发现I1来源−1<0,于是取消最老违例I1和年轻后缀。I0不取消,它保留刚得到的地址4和数据17。第7轮I0先退休,I1的新代次再从M读17;第8轮I2算出35、I3从2转发35。最终三种模式都提交load值17、35,M成为 {4:17,8:35}。
逐项解释第6轮之后I2的旧数据7、I3的旧结果7为什么都不能留下。不要只交最终M:先有错误暂定值、随后取消、最后正式提交,正是本实验要展示的差别。两种策略都到第10轮完成,因此这里只证明少做或多做了多少load,没有测出真实CPU加速比。
四、用地址传播、遮蔽与坏实现检验恢复边界
做三个迁移,分别保留完整证据:
- 把I2地址和I3地址都改成
Ref(1),I2数据仍为Ref(1,2,1),初始M[3]=M[17]=0。第一次年轻地址为3,恢复后必须全部重算成17。最终M是{3:0,4:17,17:35},槽3不能留下7或35 - 改成晚到S0写槽4=17、早到S1写槽4=23、L2读槽4。L2从1读23;S0随后揭晓同址时,来源1并不小于store年龄0,重放次数应为0。说明中间的S1怎样遮住S0
- 只保留晚到S0写槽4=3和L1读槽4,初始M[4]=3。虽然读值碰巧正确,来源−1<0仍触发1次重放。协议按来源身份判定,没有实施值相等消除
再检查下载程序真正运行的两个外部错误子类。NoReplay删除违例集合,提交load值3、7,最终M[8]=7;OnlyLoad只重置违例load,提交load值17、7,M[8]仍为7。后者说明“我已重读load”不等于“我已纠正所有消费者”。这些坏实现运行时关闭参考器自带前缀校验,由另外的顺序结果比较揭示差异,不把检测器主动抛错冒充反例轨迹。
最后写退休前缀证明:若load漏读了真正最近的同址老store,那么该store地址揭晓时必会发现它;store又必须比该load先退休,错误load实例不能先越过提交边界。对依赖旧load结果的地址和数据,还要使用“取消整个年轻后缀”这一条件。只证明来源查询,不能替代这部分生命周期论证。
五、预测集合、等待前驱和数据来源各交一份
保留主例冷启动学到的SSIT,再以相同初始内存运行第二次。热身后初始I1前驱为I0,第1–5轮产生5次 predicted-store 等待尝试;第6轮I0完成,I1转发17。两次load只执行两次、没有重放,仍到第10轮退休完成。I3另有5次匹配数据等待尝试;这两组尝试不能相加后冒充10个硬件停顿周期。
保持PC不变,把I0改写槽9。已学关系仍让I1等I0,但I1真实应读槽4旧值3,I3应读7;最终M为 {4:3,8:7,9:17}。指出这是正确但不必要的预测等待。再对确实训练过的历史调用清除,重跑原例:回到1次重放、4次load执行,而不是把另一个天然为空的实例当成清表测试。
还要查看 examples.live_clear:在活动窗口中先让已知地址的I1因为训练前驱0而WAIT,随后调用 clear_history()。SSIT、LFST和所有预测前驱被清空,但M与I1地址4不变。I1现在读到3,I0地址后到触发恢复并重新训练;重新执行和退休仍得到17、35。提交清前、清后、重训后三份状态。这条手动事件轨迹不报告自动驱动轮数性能。
合并实验用A=0x300、X=0x304形成集合0,B=0x308、Y=0x30c形成集合1,再训练A与Y。精确全表合并后四个PC都为0。新窗口为S0(A)写槽0=5、S1(B)写槽1=6、L2(X)读槽2,前驱为 [无,0,1]:
- S1字段先齐,也必须等S0
- S0完成时LFST[0]仍是1,不能清掉它
- S1完成时才删除该LFST项
- L2实际查询槽2,返回来源−1、值0,而不是从预测前驱1拿到6
- 最后全部退休,M为
{0:5,1:6},已提交load为{2:0}
公开JSON的 examples.merge包含真实load动作及全部退休,不只打印预设前驱表。解释集合号0、前驱年龄1和实际来源−1为什么回答了三个不同问题。再证明每条预测边都指向更老store,所以它不会形成等待环;同时说明空历史为什么仍可能漏掉真实冲突,独立违例恢复不能删除。
六、交付测试账本和真实费用
参考器的公开检查应报告29,524次纯来源查询、7,200次随机程序运行、113,453个驱动轮、1,043次重放及19次非法动作或输入拒绝。随机程序含来自更老load的地址和数据表达式,跨三种模式、最老/最年轻两种load选择次序,并包含已有预测关系。每次退休都对照另写的顺序解释器前缀;只比最终M会漏掉错误提交后又被覆盖的写。
这些数字是固定种子和当前有限生成范围的结果,不是所有输入的穷举证明,也不是非作者独立审查结论。你自己的交付应记录范围、种子、失败轨迹和正常/优化模式结果;增加迁移时不要继续照抄旧计数。判断错误变体时还要确认关闭诊断后,运行逻辑本身确实产生错误观察。
费用表至少区分下面几层:q个较老store的数学选择可线性扫描;公开纯接口还校验m个内存项并排序未知年龄,最坏为
事件日志、SSIT排序展示和每次退休的顺序前缀快照另计。默认前缀快照可占
回到路线时,你应能分别说明:读值正确靠最近来源规则,推测安全靠检测、完整恢复和顺序退休,预测历史只改变许可时机。这三层接好以后,才有资格在更具体的硬件模型里讨论收益。