Total Store Orderand(TSO) the x86 MemoryModel

2024-05-10 20:36

本文主要是介绍Total Store Orderand(TSO) the x86 MemoryModel,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一种广泛实现的内存一致性模型是总store顺序 (total store order, TSO)。

  • TSO 最早由 SPARC 引入,更重要的是,它似乎与广泛使用的 x86 架构的内存一致性模型相匹配。
  • RISC-V 还支持 TSO 扩展 RVTSO,部分是为了帮助移植最初为 x86 或 SPARC 架构编写的代码。 

为什么需要TSO

        处理器核心长期以来一直使用 write buffer (或 store buffer,写入缓冲区) 来保存已提交(retired)的store,直到内存系统的其余部分可以处理这些store。

        当store commit时,store进入写入store buffe,当要写入的块处于读写 coherence 状态的高速缓存中时,store退出写入缓冲区。

  • Significantly, a store can enter the write buffer before the cache has obtained read-write coherence permissions for the block to be written;
  • the write buffer thus hides the latency of servicing a store miss;

        对于单核处理器,可以通过确保对地址 A 的load将最近store的值返回给 A,即使对 A 的一个或多个store在write buffer中,也可以使write buffer在架构上不可见。

  • This is typically done by either bypassing the value of the most recent store to A to the load from A,其中“最近”由程序顺序确定,或者如果到 A 的store在write buffer中,则停止load A 。

        在构建多核处理器时,使用多个核心似乎很自然,每个核心都有自己的旁路写入缓冲区(bypass write buffer),并假设写入缓冲区在架构上仍然是不可见的。

        但是,这个假设是错误的!  

        考虑如上的代码,假设multicore processor with in-order cores,其中每个核心都有一个单入口写入缓冲区并按以下顺序执行代码;

  • 1. 核心 C1 执行store S1,但在其write buffer中缓冲新store的 NEW 值。
  • 2. 同样,核心 C2 执行store S2 并将新store的 NEW 值保存在其write buffer中。
  • 3. 接下来,两个核心执行各自的load L1 和 L2,并获得旧值 0。
  • 4. 最后,两个核心的write buffer使用新store的值 NEW 更新内存。

        最终结果是 (r1, r2) = (0, 0)。正如我们在上一章中看到的,这是 SC 禁止的执行结果。

  • 没有write buffer,硬件就是 SC,但有了write buffer,它就不是了,这使得write buffer在多核处理器中在架构上是可见的 

如何解决这个问题?

  • 对可见的写入缓冲区的一种响应是关闭它们,但由于潜在的性能影响,供应商一直不愿这样做。另一种选择是使用激进的、推测性的 SC 实现,使写入缓冲区再次不可见,但这样做会增加复杂性,并且会浪费电力来检测违规和处理错误推测。
  • SPARC 和后来的 x86 选择的选项是放弃 SC,转而支持memory consistency model,允许在每个核心上直接使用先进先出 (FIFO)  write buffer。这个被称为 TSO 的新模型允许结果“(r1, r2) = (0, 0)”。这个模型让一些人感到惊讶,但事实证明,对于大多数编程习惯来说,它的行为就像 SC 一样,并且在所有情况下都得到了明确的定义。

TSO/X86 的基本思想

随着执行的进行,SC 要求每个核心为连续操作的所有四种组合保留其load和store的程序顺序:

  • * Load -> Load
  • * Load -> Store
  • * Store -> Store (意味着写入缓冲区必须是 FIFO(而不是,例如,合并)以保持 store-store 顺序。)
  • * Store -> Load (Included for SC but omitted for TSO) 

TSO 包括前三个约束,但不包括第四个;

同样考虑上面的那个例子:

怎么阻止(d)这种场景的发生?

  • 插入fence指令;
  • 使用 TSO 的程序员很少使用 FENCE(又名内存屏障),因为 TSO 对大多数程序“做正确的事”。

write buffer的bypassing

TSO 确实允许一些非直观的执行结果。表 4.3 是表 4.1 中程序的修改版本,其中:

  • 核心 C1 和 C2 分别制作 x 和 y 的本地副本。
  • 许多程序员可能假设如果 r2 和 r4 都等于 0,那么 r1 和 r3 也应该为 0,因为store S1 和 S2 必须在load L2 和 L4 之后插入到内存顺序中。

然而,图 4.3 展示了一个执行,显示 r1 和 r3 绕过每个核心写入缓冲区中的值 NEW。

事实上,为了保持single-thread sequential semantics,每个核心都必须按照程序顺序看到自己store的效果,即使store还没有被其他核心观察到。因此,在所有 TSO 执行下,本地副本 r1 和 r3 将始终设置为 NEW 值。

TSO与SC的差异

1. 所有核心都将它们的load和store插入到内存顺序 `<m` 中,考虑到它们的程序顺序,无论它们是相同还是不同的地址(即 a==b 或 a!=b)。有四种情况:

* If `L(a) <p L(b)` => `L(a) <m L(b)` /* Load -> Load */
* If `L(a) <p S(b)` => `L(a) <m S(b)` /* Load -> Store */
* If `S(a) <p S(b)` => `S(a) <m S(b)` /* Store -> Store */
* 删除:If `S(a) <p L(b)` => `S(a) <m L(b)` /* Store -> Load */ => 修改1:Enable FIFO Write Buffer

2. 每个load从它之前的最后一个store中获取它的值到相同的地址:

* 删除:Value of `L(a)` = Value of `MAX <m {S(a) | S(a) <m L(a)}` => 修改2:Need Bypassing
* Value of `L(a)` = Value of `MAX <m {S(a) | S(a) <m L(a) or S(a) <p L(a)}`

最后一个令人费解的等式表明,load的值是最后store到同一地址的值,该地址要么是(a)按内存顺序在它之前,要么(b)按程序顺序在它之前(但可能在它之后)内存顺序,选项(b)优先(即,write buffer绕过覆盖内存系统的其余部分)。

3. 第 (1) 部分必须扩充以定义 FENCE:/* 修改3: FENCEs Order Everything */

* If `L(a) <p FENCE` => `L(a) <m FENCE`   /* Load -> FENCE */
* If `S(a) <p FENCE` => `S(a) <m FENCE`   /* Store -> FENCE */
* If `FENCE <p FENCE` => `FENCE <m FENCE` /* FENCE -> FENCE */
* If `FENCE <p L(a)` => `FENCE <m L(a)`   /* FENCE -> Load */
* If `FENCE <p S(a)` => `FENCE <m S(a)`   /* FENCE -> Store */

因为 TSO 已经需要除 Store -> Load 之外的所有顺序,也可以将 TSO FENCE 定义为仅排序:

* If `S(a) <p FENCE` => `S(a) <m FENCE` /* Store -> FENCE */
* If `FENCE <p L(a)` => `FENCE <m L(a)` /* FENCE -> Load */

TSO的实现

TSO/x86 的实现问题与 SC 类似,只是增加了每个核心中的 FIFO writer buffer。

  • Load 和 store 按照po的顺序,离开该core;

  • Load 要么 bypass write buffer 中的值,要么像以前一样等待切换。

  • 如果 buffer 未满,则 store 进入 FIFO write buffer 的尾部;如果 buffer 已满,则 store stall 核心。

  • 当 switch 选择核心 Ci 时,它要么执行下一次 load,要么执行 write buffer 头部的 store。

        最后,多线程为 TSO 引入了一个微妙的写入缓冲区问题。 TSO write buffer在逻辑上对每个线程上下文(虚拟内核)都是私有的。因此,在多线程内核上,一个线程上下文不应该bypassing另一个线程上下文的write buffer。这种逻辑分离可以通过每个线程上下文的写入缓冲区来实现,或者更常见的是,by using a shared write buffer with entries tagged by thread-context identifiers that permit bypassing only when tags match.

如何定义好的memory consistency model?

一个良好的内存一致性模型应当具备 Sarita Adve 提出的三个P,以及我们的第四个P:

  • *可编程性*:一个良好的模型应该使编写多线程程序变得(相对)容易。这个模型应该对大多数用户来说是直观的,即使是那些没有阅读详细细节的用户也是如此。它应该是精确的,以便专家可以拓展所允许的范围。
  • *性能*:一个良好的模型应该在合理的功耗、成本等条件下促进高性能的实现。它应该给予实现者广泛的选项余地。
  • *可移植性*:一个良好的模型应该被广泛采用,或者至少提供向后兼容性或在不同模型之间进行转换的能力。
  • *精确性*:一个良好的模型应该被精确定义,通常通过数学方式。自然语言过于模糊不清,不能让专家拓展所允许的范围。

SC和TSO模型的优劣如何?
运用这四个P来分析:

  •  *可编程性*:SC 模型是最直观的。TSO 模型也接近,因为对于常见的编程惯例,它的行为类似于 SC 模型。然而,隐含的非 SC 执行可能会对程序员和工具作者产生影响。
  •  *性能*:对于简单的核心,TSO 模型可以比 SC 模型提供更好的性能,但通过推测可以减小二者之间的差距。
  • *可移植性*:SC 模型被广泛理解,而 TSO 模型得到了广泛采用。
  • *精确性*:SC 模型和 TSO 模型都有明确的形式定义。

总的来说,SC 模型和 TSO 模型非常接近,尤其是与下一章讨论的更复杂和更宽松的 memory consistency 模型相比较。

这篇关于Total Store Orderand(TSO) the x86 MemoryModel的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/977492

相关文章

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

NGINX轻松管理10万长连接 --- 基于2GB内存的CentOS 6.5 x86-64

转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=190176&id=4234854 一 前言 当管理大量连接时,特别是只有少量活跃连接,NGINX有比较好的CPU和RAM利用率,如今是多终端保持在线的时代,更能让NGINX发挥这个优点。本文做一个简单测试,NGINX在一个普通PC虚拟机上维护100k的HTTP

App Store最低版本要求汇总

1,自此日期起: 2024 年 4 月 29 日 自 2024 年 4 月 29 日起,上传到 App Store Connect 的 App 必须是使用 Xcode 15 为 iOS 17、iPadOS 17、Apple tvOS 17 或 watchOS 10 构建的 App。将 iOS App 提交至 App Store - Apple Developer 2,最低XCode版本 Xcod

ExtMvc store不能通过xtype选择器得到的办法

store 不能通过xtype选择器得到,  init : function() {         this.control({                 'smsmenu gridpanel[name='company'] : {                                         render:function(grid,opts){

SylixOS x86平台C++符号表

1.C++跨平台问题说明     在x86平台下编译C++工程过程后,运行编译好的C++共享库时出现符号表__atomic_fetch_sub_4找不到的问题,如图 1.1所示。

《Linux运维总结:基于X86_64+ARM64架构CPU使用docker-compose一键离线部署consul 1.18.1容器版分布式ACL集群》

总结:整理不易,如果对你有帮助,可否点赞关注一下? 更多详细内容请参考:《Linux运维篇:Linux系统运维指南》 一、部署背景 由于业务系统的特殊性,我们需要面向不通的客户安装我们的业务系统,而作为基础组件中的consul 针对不同的客户环境需要多次部署集群,作为一个运维工程师,提升工作效率也是工作中的重要一环。所以我觉得有必要针对 x86_64 + ARM64 CPU架构cons

2.第二阶段x86游戏实战2-认识进制、理解数据宽度和位的概念

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 工具下载: 链接:https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd=6tw3 提取码:6tw3 复制这段内容后打开百度网盘手机App,操作更方便哦 上一个内容:1.第二阶段x86游戏实战2-前言 进制、数据宽度、位是一

x86的保护模式(一)

10月份就剩明天一天了,今天晚上突袭一下,写篇新博客  2017年10月30日18:13:23 BY :张飞online

X86汇编基础

1. mov指令及几种内存寻址方式: 寄存器模式(register mode),以%开头的寄存器标示符 立即数(immediate):是以$开头的数值 直接寻址(direct):直接访问一个指定的内存地址的数据 间接寻址(indirect):将寄存器的值作为一个内存地址来访问内存 变址寻址(displaced):在间接寻址之时改变寄存器的数值 AT&T汇编格式与Intel汇