内存一致性(Memory Consistency)模型简介

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2 为什么需要内存一致性(Memory Consistency)模型

从硬件的角度来看,最初对内存的访问过程大概是这样:

CPU <--> 内存

相对于 CPU 的执行频率,直接访问内存速度较慢,于是引入存储速度比内存更快、但更贵、更小容量的 CPU cache ,用于缓存最近访问的内存数据,硬件拓扑结构如下图(不考虑多级 cache 情形):
在这里插入图片描述
于是访问内存的过程变成了这样:

1. cahce 命中,直接从 cache 中读取
   CPU <--> CPU cache
2. cahce 未命中,从内存加载到 cache ,再从 cache 读取
   CPU <--> CPU cache <--> 内存

有了 CPU cache,速度上得到了很大提升,但人的追求永无止境,为了更快的速度,在 CPU 和 cache 之间, 又加入了更快更贵更小容量的 store buffer 存储。此时硬件拓扑如下:
在这里插入图片描述
在有 store buffer 缓存的情形下,写操作数据写入到 store buffer ,在需要的时候再写入到 cache 。
说了这么多,这和引入内存一致性模型有什么关系?来看一个例子:
在这里插入图片描述
其中:

. t1 和 t2 代表两个线程,t1 绑定在 CPU 1 上运行,t2 绑定在 CPU 2 上运行;
. 变量 x 和 y 被两个线程共享,初始值为 0

那么,最终 a 和 b 的值分别是多少呢?在没有引入内存一致性模型前,对这个问题无法讨论,接下来一一介绍几个常见的内存一致性(Memory Consistency)模型。我们将在 TSO 模型 下讨论这个例子。

3. 什么是内存一致性(Memory Consistency)模型

内存一致性模型(Memory Consistency) 包括 硬件层面的模型(由硬件负责)语言的内存模型(由编译器负责,如 DRFx)。本文只讨论 硬件层面的模型,后文出现的 内存一致性模型(Memory Consistency) 专指 硬件层面的模型
内存一致性(Memory Consistency)模型就是对内存读写(load/store)的以下4种顺序进行定义:

store-load
store-store
load-load
load-store

store-load 来举例,就是说代码中有两条指令,在顺序上,第1条时写指令(store),第2条是读指令(load),内存一致性(Memory Consistency)模型,就是从硬件设计上,是否保证严格按代码中这两条指令的先后顺序进行内存访问。这是什么意思?譬如在后面提到的 TSO(Total Store Order) 模型上下文,可能的执行情况是这样:第1条写指令(store),执行在 CPU 0 上,将变量 var 数据写入到 store buffer ;第2条读指令(load),执行在 CPU 1 上,它去读取变量 var 的数据,而这时候变量 var 的最新数据在 CPU 0 的 store buffer 里面,CPU 1 是无法看到 CPU 0 的 store buffer 的,所以读取的就不是变量 var 的最新值,这看起来,就是发生了 store-load 乱序(注意,是发生在不同 CPU 间)

4. 各种内存一致性(Memory Consistency)模型

4.1 顺序一致性(SC: Sequential Consistency)模型

所有 章节 3 中提到的4种存储操作,严格按照代码顺序执行。在 顺序一致性(SC: Sequential Consistency)模型 下, 章节 3 例子中最终 a = 1, b = 1

4.2 完全存储定序(TSO: Total Store Order)模型

完全存储定序(TSO: Total Store Order)模型 下,写操作(store) 不会将数据立即写入内存,而是先写到按严格先入先出(FIFO)store buffer 队列
章节 3 中提到的4种存储操作,允许 store-load 的写读存储操作乱序,其它操作保序。但要了解的是,这种乱序是指不同 CPU 之间的乱序:因为数据写到 CPU 自身的 store buffer ,而 直到将 CPU 自身 store buffer 中的数据刷出之前, CPU 自身 store buffer 中的数据不会被其它 CPU 看到;但是如果是 CPU 自身发起的读操作(load),则是可以看到写入到 store buffer 的最新值的。CPU 何时将 store buffer 中的数据刷出,从我目前了解的资料,都没有一个明确的界定(TODO: )
了解了 完全存储定序(TSO: Total Store Order)模型,我们来分析 章节 3 中例子 a,b 最终值 得一种可能的情形:
在这里插入图片描述
其中:

. 左边第1列表示指令执行顺序序列
. t1, t2 列分别表示绑定在 CPU 1,2 中执行的线程
. WB1 为 CPU 1 的 store buffer ,WB2 为 CPU 2 的 store buffer
  WBx 列 中的形如 [(x,1)] 表示 store buffer 队列中,缓存的变量 (x的地址,x的值1) 的二元组
. Main Memory 复合列表示各变量(x,y,a,b)在内存中的值

我们看到,在执行步骤 6 中,t1 (CPU 1) 将 store buffer 中的 (x,1) 出队,即将 x 的值1写入内存(Main Memory),但很可惜,执行步骤 5 中, t2 (CPU 2) 从内存读取的 x 值是 0 (这时候 CPU 的 store buffe 缓存的值1 不能被 CPU 0 看到),导致了 b 读取的 x 值为 0;类似的,a 读到的 y 的值也为 0 。但无论如何,上面分析的值只是 TSO 模型下,a, b 值的其中一种可能性。由于 CPU store buffer 中数据出列时间的不确定性,所以 a, b 最终值,从理论上分析,可能组合包括 (a=0,b=0), (a=1,b=0), (a=0,b=1), (a=1,b=1) 这4种可能性。
对 TSO 模型的介绍,暂时到此为止。如果想了解更多关于 TSO 模型的细节,可参考文章 《Taming TSO Memory Consistency with Models》

4.3 部分存储定序(PSO: Part Store Order)模型

章节 3 中提到的4种存储操作,允许 store-load,store-store 的存储操作乱序,其它操作保序。在 完全存储定序(TSO: Total Store Order)模型 下,我们例子中进程 B 中 temp 变量的值最后可能为 0 或 1 。

4.4 宽松存储(RMO: Relax Memory Order)模型

章节 3 中提到的4种存储操作,允许所有4种操作乱序。在 完全存储定序(TSO: Total Store Order)模型 下,我们例子中进程 B 中 temp 变量的值最后可能为 0 或 1 。

5. 内存屏障(memory barrier)

为了解决内存操作乱序引入的问题,引入了 内存屏障 (memory barrier) 。如 ARM 的 DMB, DSB, ISB 指令等,更多关于 内存屏障 (memory barrier) 的细节将不在此处展开,感兴趣的读者可查找相关资料。

6. 参考资料

《perfbook.2018.12.08a.pdf》
《Taming TSO Memory Consistency with Models》

https://zhuanlan.zhihu.com/p/422848235
https://blog.csdn.net/anyegongjuezjd/article/details/125954805

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
多线程内存模型是指在多线程环境下,不同线程之间共享的内存模型。在多线程编程中,多个线程可以同时访问和修改同一个共享变量,但由于线程之间的并发执行,可能会出现一些并发问题,如数据竞争、原子性问题等,因此需要通过内存模型来规定多线程中共享变量的访问和修改规则,以保证线程之间的正确协作。 常用的多线程内存模型有两种:顺序一致性内存模型和Java内存模型(Java Memory Model,JMM)。 顺序一致性内存模型是指对于每个线程来说,该线程的所有操作都是按照程序的顺序执行的,且所有线程之间的操作是按照全局顺序来执行的。这种内存模型相对简单,易于理解,但对程序的执行速度有一定的限制。 Java内存模型是针对Java语言的多线程内存模型。Java内存模型是基于顺序一致性内存模型的,但相对于顺序一致性内存模型,Java内存模型允许一定程度上的重排序,以提高程序的执行效率。Java内存模型主要定义了共享变量的访问规则,如可见性、原子性等,并通过使用volatile关键字和synchronized关键字等机制来实现线程之间的同步与协作。 对于多线程内存模型的理解和正确使用,对于编写高效且正确的多线程程序至关重要。在编写多线程程序时,需要根据具体需要选择合适的内存模型,并遵循相应的编程规范和约定,以确保多线程程序的正确性和可靠性。此外,还可以利用锁、原子类、线程安全的数据结构等工具来保证多线程程序的正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值