网站首页 > 文章精选 正文
首先这三种技术的出现场景均是为了避免内存访问过程中出现一些不符合预期的行为。他们的作用有相似之处,但也有不同的细分场景,可以通过下面这张表先来做个简单总结
抑制编译器重排
比如我们有如下代码
编译器在生成目标代码过程中发现上面的两行代码彼此之间没有关系,因此编译器不保证汇编代码中p_a的写入一定在p_b读取之前。如果这个顺序对你来说是有意义的,你可以通过一些手段来防止编译器做重排
- 把对应的变量设置为volatile, C++保证对volatile变量之间的访问不会做重排,仅仅是volatile变量之间,但是不保证volatile变量与其他变量之间
- 在需要的地方手动添加合适的memory barrier(内存栅栏), 他可以保证编译器不会进行错误重排
- 把对应的变量声明为atomic, 他的作用和volatile类似,C++也保证atomic变量之间的访问不会进行重排。
抑制编译器优化
主要指编译器不生成他自己认为无意义的内存访问代码,比如下面
在这里,a这个变量似乎不会起任何效果,因此对a的内存访问都会被优化掉。在这种情况下,f()生成汇编会跟空函数差不多。但是如果此时需要对a循环若干次,我们就需要通过一些行为来防止编译器进行优化。
- 可以对变量声明为volatile / atomic, C++保证对这种变量的访问肯定会发生,因此不会被优化掉
需要注意的是,针对这种case, 手动添加内存屏障mfence是没有意义的,因为他仅仅是让循环不被优化,但是内部对a的访问仍然会被优化掉。
抑制CPU乱序
上面说到了编译器重排,但是要记住一点的就是,即使没有编译器重排,内存访问也不一定会按照我们代码中的顺序进行执行。因为现代CPU有诸多特性会影响这个行为,对应不同架构的CPU来说,其所保证的内存存储模型是不一样的。比如x86_64就会所谓的TSO(完全存储定序)模型,而很多的ARM则是RMO(宽松存储模型),再加上多核间Cache一致性问题,多线程编程会面临更多的挑战。
为了解决这些问题,要从根本上来通过插入Memory Barrier内存屏障指令来解决,这些会指令会让CPU保持特定的内存访问顺序和内存写入操作系统在多核间的可见性。然而由于不同处理器架构件的内存模型和具体Memory Barrier指令均不同,需要在什么位置添加什么指令并不具有通用性。因此C++ 11在此基础上做了一层抽象,引入了atomic类型以及Memory Order的概念,有助于写出更通用的代码。从本质上atomic的memory order看就是编译器来帮我们根据代码中的更高层次的Memory Order来自动选择插入特定处理器的内存屏障指令。
保证访问原子性
所谓访问原子性就是Read / Write是否存在中间状态。具体如何实现原子性的访问跟处理器的指令集有很大的关系。如果处理器本身就支持这些原子操作,比如Atomic Store, Atomic Load, Atomic Fetch Add, Atomic Compare And Swap(CAS), 那只需要在代码生成时选择合适的指令即可,否则就需要依赖锁来帮助解决。C++提供的这些可移植的通用方法其实就是std::atomic。volatile以及Memory Barrier与此都无关
总结
从上面的比较中可以看出,volatile, atomic, Memory Barrier的范围还是比较区分的:
- 如果需要原子性的访问支持,只能选择atomic
- 如果仅仅只是需要保证内存访问不会被编译器优化掉,优先考虑volatile
- 如果需要保证memory order, 也可以考虑使用atomic, 只有当不需要保证原子性,而且需要明确要在哪里选择插入内存屏障的时候才考虑手动插入Memroy Barrier.
- 上一篇: modern c++函数修饰符,限定符,说明符总结
- 下一篇: C语言精华:C标准库高级用法深度解析
猜你喜欢
- 2025-05-14 嵌入式开发中宝藏级别的C语言代码,使用频率高,绝对值得珍藏
- 2025-05-14 嵌入式面试常问的16个C语言问题
- 2025-05-14 如何利用CAS技术实现无锁队列
- 2025-05-14 并发编程:从线程到协程的技术演进与实战指南
- 2025-05-14 嵌入式工程师竟然看不懂这些专业语句,那真别怪人说你菜
- 2025-05-14 CPU缓存一致性:从理论到实战
- 2025-05-14 Java 魔法类 Unsafe 详解
- 2025-05-14 为QML创建C++插件
- 2025-05-14 C++ Qt开发:运用QThread多线程组件
- 2025-05-14 教你用C来实现基于Mempool的内存池设计
- 05-14TS,TypeScript,Windows环境下构建环境,安装、编译且运行
- 05-14TypeScript 也能开发AI应用了!
- 05-14搞懂 TypeScript 装饰器
- 05-14前端小哥哥:如何使用typescript开发实战项目?
- 05-14在 React 项目中,一般怎么处理错误?
- 05-14react19 常用状态管理
- 05-14Vue3开发极简入门(2):TypeScript定义对象类型
- 05-14C#与TypeScript语法深度对比
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)
- mysql数据库面试题 (57)