CPU体系(1):内存模型 & CPU Cache一致性 (待整理)
C++中的 volatile, atomic, memory barrier 应用场景对比
| -- | volatile | memory barrier | atomic |
|---|---|---|---|
| 抑制编译器重排 | Yes | Yes | Yes |
| 抑制编译器优化 | Yes | No | Yes |
| 抑制 CPU 乱序 | No | Yes | Yes |
| 保证访问原子性 | No | No | Yes |
- 如果需要原子性的访问支持,只能选择 atomic;
- 如果仅仅只是需要保证内存访问不会被编译器优化掉,优先考虑 volatile;
- 如果需要保证 Memory Order,也优先考虑 atomic,只有当不需要保证原子性,而且很明确要在哪插入内存屏障时才考虑手动插入 Memory Barrier。
存储一致性 vs 缓存一致性
存储一致性(memory consistency),不要跟缓存一致性(cache coherence)混淆了。
缓存一致性协议解决的是对单个存储器地址的访问之间如何排序的问题,而对于不同地址的访问并不是缓存一致性协议所要考虑的问题。存储一致性问题在任何具有或不具有高速缓存的系统中都存在,虽然高速缓存的存在有可能进一步加剧存储一致性问题。
存储器模型(memory model)又称为存储一致性模型。用于定义系统中对存储器访问需要遵守的原则,只要软件和硬件都遵循该原则,就能保证多核程序能运行得到确切的结果。Memory model一致性问题来源于:编译期乱序、执行期乱序,以及Cache不同步。
个人理解:内存模型与CPU Cahce一致性:由于CPU core对store buffer/invalidate queue与对Cache的操作是异步的。对于写操作(release语义),需要等待该cpu core的 store buffer 全部写到Cache,并等待MESI操作同步完成;对于读操作(aquire语义),需要等待该清空该CPU Core的invalidate queue,以保证该CPU Core从内存中读取数据。ref 。

多CPU/Core MESI协议局限性
当一个CPU进行写入时,首先会给其它CPU发送Invalid消息,然后把当前写入的数据写入到Store Buffer中。然后异步在某个时刻真正的写入到Cache中。当前CPU核如果要读Cache中的数据,需要先扫描Store Buffer之后再读取Cache。但是此时其它CPU核是看不到当前核的Store Buffer中的数据的,要等到Store Buffer中的数据被刷到了Cache之后才会触发失效操作。而当一个CPU核收到Invalid消息时,会把消息写入自身的Invalidate Queue中,随后异步将其设为Invalid状态。和Store Buffer不同的是,当前CPU核心使用Cache时并不扫描Invalidate Queue部分,所以可能会有极短时间的脏读问题。MESI协议,可以保证缓存的一致性,但是无法保证实时性。所以我们需要通过内存屏障在执行到某些指令的时候强制刷新缓存来达到一致性。
STL定义的 memory models
| value | memory model | description |
|---|---|---|
| memory_order_relaxed | Relaxed |
没有同步或顺序制约,仅对此操作要求原子性 |
| memory_order_consume | Consume |
1. 对当前要读取的内存施加 release 语义(store),在代码中这条语句后面所有与这块内存有关的读写操作都无法被重排到这个操作之前; 2. 在这个原子变量上施加 release 语义的操作发生之后,consume 可以保证读到所有在 release 前发生的并且与这块内存有关的写入 |
| memory_order_acquire | Acquire |
1. 向前保证,本线程中所有读写操作都不能重排到memory_order_acquire的load之前; 2. 其他线程中所有memory_order_release的写操作都对当前线程可见 |
| memory_order_release | Release |
1. 向后保证,本线程中所有读写操作都不能重排到memory_order_acquire的store之后; 2. 本线程中的所有写都对其他对同一atomic变量带有 memory_order_acquire的线程可见; 3. 本线程中的所有写都对其他所有有依赖且consume该变量的线程可见 |
| memory_order_acq_rel | Acquire/Release |
1. 是release+acquire的结合,前后都保证,本线程中所有读写操作既不能重排到memory_order_acquire的load之前也不能到之后; 2. 其他线程的memory_order_release写在本线程写之前都是可见的; 3. 本线程的的写对其他线程的memory_order_acquire读都是可见的 |
| memory_order_seq_cst | Sequentially consistent |
1. 顺序一致性; 2. 如果是读取就是 acquire 语义,如果是写入就是 release 语义,如果是读取+写入就是 acquire-release 语义; 3. 同时会对所有使用此 memory order 的原子操作进行同步,所有线程看到的内存操作的顺序都是一样的,就像单个线程在执行所有线程的指令一样 |

常使用有三种情形:relaxed order,release/acquire,cst。std::memory_order, memory order再探。C++17开始consume被弃用,自动升级为acquire。
进一步阅读:
- std::memory_order
- Sequential Consistency,Cache-Coherence及Memory barrier
- ARM系列 - - 存储模型(一)
- 处理器设计 -- 顺序与乱序
- Memory Model: 从多处理器到高级语言
- 内存模型与c++中的memory order
- CPU多级缓存
- 從硬體觀點了解 memory barrier 的實作和效果
ARM fence 指令
- 数据存储器隔离指令 DMB。指令保证:仅当所有在它前面的存储器访问操作都执行完毕后,才提交(commit)在它后面的存储器访问操作。
- 数据同步隔离指令 DSB。比DMB严格:仅当所有在它前面的存储器访问操作都执行完毕后,才执行在它后面的指令(亦即任何指令都要等待存储器访问操作——译者注)
- 指令同步隔离 ISB。最严格:它会清洗流水线,以保证所有它前面的指令都执行完毕之后,才执行它后面的指令。
CPU指令乱序来自流水线中的发射(Issue)/执行(取指、解码、发射/执行、取数、写回)阶段,其他阶段都是顺序DMB、DSB 和 ISB指令的深度解读。
out-of-order的CPU以及有多个pipeline的CPU,都会产生指令执行乱序。更多资料:
C++11 的单例模式线程安全
class A {
public:
static A* instance() {
static A* ptr = new A();
return ptr;
}
};
int main(int argc, char *argv[]) {
auto ptr = A::instance();
return 0;
}
使用Compiler Explore查看,插入了一个guard variable标记是否初始化;
并且调用guard aquire/release 保证初始化的原子性。
参考
CPU体系(1):内存模型 & CPU Cache一致性 (待整理)的更多相关文章
- Java内存模型_顺序一致性
数据竞争: 当程序未正确同步时,就会存在数据竞争.java内存模型规范对数据竞争的定义如下: 在一个线程中写一个变量 在另一个线程读同一个变量 而且写和读没有通过同步来排序 如果程序是正确同步的,程序 ...
- 从 CPU 讲起,深入理解 Java 内存模型!
Java 内存模型,许多人会错误地理解成 JVM 的内存模型.但实际上,这两者是完全不同的东西.Java 内存模型定义了 Java 语言如何与内存进行交互,具体地说是 Java 语言运行时的变量,如何 ...
- 再有人问你Java内存模型是什么,就把这篇文章发给他
前几天,发了一篇文章,介绍了一下JVM内存结构.Java内存模型以及Java对象模型之间的区别.有很多小伙伴反馈希望可以深入的讲解下每个知识点.Java内存模型,是这三个知识点当中最晦涩难懂的一个,而 ...
- 深入理解java内存模型
深入理解Java内存模型(一)——基础 深入理解Java内存模型(二)——重排序 深入理解Java内存模型(三)——顺序一致性 深入理解Java内存模型(四)——volatile 深入理解Java内存 ...
- Java 并发系列之三:java 内存模型(JMM)
1. 并发编程的挑战 2. 并发编程需要解决的两大问题 3. 线程通信机制 4. 内存模型 5. volatile 6. synchronized 7. CAS 8. 锁的内存语义 9. DCL 双重 ...
- 02 java内存模型
java内存模型 1.JVM内存区域 方法区:类信息.常量.static.JIT (信息共享) java堆:实例对象 GC (信息共享) OOM VM stack:JAVA方法在运行的内存模型 (OO ...
- 内存模型与c++中的memory order
概 c++的atomic使用总会配合各种各样的memory order进行使用,memory order控制了执行结果在多核中的可见顺序,,这个可见顺序与代码序不一定一致(第一句代码执行完成的结果不一 ...
- JVM内存模型、指令重排、内存屏障概念解析
在高并发模型中,无是面对物理机SMP系统模型,还是面对像JVM的虚拟机多线程并发内存模型,指令重排(编译器.运行时)和内存屏障都是非常重要的概念,因此,搞清楚这些概念和原理很重要.否则,你很难搞清楚哪 ...
- JVM内存模型、指令重排、内存屏障概念解析(转载)
在高并发模型中,无是面对物理机SMP系统模型,还是面对像JVM的虚拟机多线程并发内存模型,指令重排(编译器.运行时)和内存屏障都是非常重要的概念,因此,搞清楚这些概念和原理很重要.否则,你很难搞清楚哪 ...
- Java并发(二):Java内存模型
一.硬件内存架构 一个现代计算机通常由两个或者多个CPU.其中一些CPU还有多核.每个CPU在某一时刻运行一个线程是没有问题的.如果你的Java程序是多线程的,在你的Java程序中每个CPU上一个线程 ...
随机推荐
- 高阶 CSS 技巧在复杂动效中的应用
最近我在 CodePen 上看到了这样一个有意思的动画: 整个动画效果是在一个标签内,借助了 SVG PATH 实现.其核心在于对渐变(Gradient)的究极利用. 完整的代码你可以看看这里 -- ...
- sklearn中MLPClassifier源码解析
神经网络 .fit() 首先传入类私用方法._fit() 确定hidden_layer_size是可迭代的 调用_validate_hyperparameters验证超参数是否合法 验证输入的x和y是 ...
- Linux宝塔后台管理地址使用SSL,并部署非443端口的https
上传你的key和pem,然后点设置 点击配置文件 插入代码 1 ssl on; 2 ssl_certificate /xxx/yyy/zzz.pem; 3 ssl_certificate_key /x ...
- C++ 左值引用与一级指针
将**左值引用**用于**一级指针**时,有以下几种用法: ```c++ //方式一:引用一级指针,常规用法 int a = 5; int * pa = &a; int * &rpa ...
- 管理数据库的神器:DBeaver
转载自:https://mp.weixin.qq.com/s/oqEAZNk-TorqRUocav27Ew GitHub地址:https://github.com/dbeaver/dbeaver 软件 ...
- 修改Windows10 命令终端cmd的编码为UTF-8 && IDEA控制台输出中文乱码
临时修改 进入cmd窗口后,直接执行 chcp 65001 永久修改 在运行中输入regedit,找到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Pro ...
- Gitlab基础知识介绍
GitLab架构图 Gitlab各组件作用 -Nginx:静态web服务器. -gitlab-shell:用于处理Git命令和修改authorized keys列表. -gitlab-workhors ...
- centos下如何获取某个命令的源代码?
以ls命令为例,其它命令类似: 1.利用which命令获取命令可执行文件的位置: [root@228 /]# which ls alias ls='ls --color=auto' /usr/bin/ ...
- Jpa常用API
service中执行sql 根据请求参数拼接sql import javax.persistence.Query; import javax.persistence.EntityManager; @A ...
- 制造企业有可能自行开发ERP系统吗?
当然可以啊! 生产企业对于ERP的需求是一直存在的,但市场上多为标准化的产品,与企业的个性化需求矛盾着. 有很多制造企业自行开发ERP系统啊!只是各个企业成效不同而已,毕竟不同企业的IT开发能力不同而 ...