C++ volatile实现原子性读写

__declspec(selectany) 支持h文件定义全局变量,重复包含不报错

int i = 2; //变量i还是不用加volatile修饰

#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))

#define READ_ONCE(x) ACCESS_ONCE(x)

#define WRITE_ONCE(x, val) ({ ACCESS_ONCE(x) = (val); })

a = READ_ONCE(i);

WRITE_ONCE(i, 2);

volatile仅仅是保证int的地址对齐,而对齐后的×××在现代处理器中,是能够做到原子性读写的。
在C++中volatile具有以下特性:

  1. 易变性:
    所谓的易变性,在汇编层面反映出来,就是两条语句,
    下一条语句不会直接使用上一条语句对应的volatile变量的寄存器内容,而是重新从内存中读取。
  2. "不可优化"性:
    volatile告诉编译器,不要对我这个变量进行各种激进的优化,甚至将变量直接消除,保证程序员写在代码中的指令,一定会被执行。
  3. “顺序性”:
    能够保证Volatile变量间的顺序性,编译器不会进行乱序优化。Volatile变量与非Volatile变量的顺序,编译器不保证顺序,可能会进行乱序优化。

//CAS作为最基础的RMW操作,其他所有RMW操作都可以通过CAS来实现
bool CAS( int * pAddr, int nExpected, int nNew )
atomically {
if ( *pAddr == nExpected ) {
*pAddr = nNew ;
return true ;
}
else
return false ;
}

//CAS变种
int CAS( int * pAddr, int nExpected, int nNew )
atomically {
if ( *pAddr == nExpected ) {
*pAddr = nNew ;
return nExpected ;
}
else
return *pAddr;
}

//例如 fetch-and-add(FAA)
int FAA( int * pAddr, int nIncr )
{
int ncur = *pAddr;
do {} while ( !compare_exchange( pAddr, ncur, ncur + nIncr ) ;//compare_exchange失败会返回当前值于ncur
return ncur ;
}

在C++11的原子lib中,主要有以下RMW操作:
std::atomic<>::fetch_sub()
std::atomic<>::fetch_and()
std::atomic<>::fetch_or()
std::atomic<>::fetch_xor()
std::atomic<>::exchange()
std::atomic<>::compare_exchange_strong()
std::atomic<>::compare_exchange_weak()

//实现自己需要的原子RMW操作,我们需要一个原子对内存中值执行乘法,也就是 atomic fetch_multiply
uint32_t fetch_multiply(std::atomic<uint32_t>& shared, uint32_t multiplier)
{
uint32_t oldValue = shared.load();
while (!shared.compare_exchange_weak(oldValue, oldValue * multiplier))
{
}
return oldValue;
}

//对结构体进行原子修改
struct Terms
{
uint32_t x;
uint32_t y;
};
std::atomic terms;
void atomicFibonacciStep()
{
Terms oldTerms = terms.load();
Terms newTerms;
do
{
newTerms.x = oldTerms.y;
newTerms.y = oldTerms.x + oldTerms.y;
}
while (!terms.compare_exchange_weak(oldTerms, newTerms));
}

//是不是在内部加了锁
std::atomic<> template包含了一个is_lock_free()成员来用于判断该原子类型是原子操作是否是lock-free的。
—同时满足以下条件的原子类的原子操作才能做出是lock-free的保证

  1. The compiler is a recent version MSVC, GCC or Clang.
  2. The target processor is x86, x64 or ARMv7 (and possibly others).
  3. The atomic type is std::atomic<uint32_t>, std::atomic<uint64_t> or std::atomic<T*> for some type T.

我们必须加上编译器的barrier来防止编译器的乱序优化:
#define COMPILER_BARRIER() asm volatile(“” ::: “memory”)
int Value;
int IsPublished = 0;
void sendValue(int x)
{
Value = x;
COMPILER_BARRIER(); // prevent reordering of stores
IsPublished = 1;
}

int tryRecvValue()
{
if (IsPublished)
{
COMPILER_BARRIER(); // prevent reordering of loads
return Value;
}
return -1; // or some other value to mean not yet received
}

//cache同步
Write Invalidate(置无效):当一个CPU Core修改了一份数据X,那么它需要通知其他core将他们的cache中的X设置为无效(invalid)
Write Update(写更新):当一个CPU Core修改了一份数据X,那么它需要通知其他core将他们的cache中的X更新到最新值(如果cache中有的话)

//cache间MESI协议就包含了描述共享的状态
M(Modified): cache line数据有效,但是数据被修改过了,本Cache中的数据是最新的,内存的数据是老的,需要在适当时候将Cache数据写回内存。该cache将数据及其控制权传递到其他cache中,或者cache需要负责将数据写回到memory中,而这些操作都需要在reuse该cache line之前完成。
E(Exclusive):cache line数据有效,并且cache和memory中的数据是一致的,同时数据只在本cache中有效。直接reuse该cacheline(将cacheine中的数据丢弃,用作他用)
S(Shared):cache line的数据有效,并且cache和memory中的数据是一致的,同时该数据在多个cpu cache中也是有效的。直接reuse该cacheline(将cacheine中的数据丢弃,用作他用)
I(Invalid):本cache line的数据已经是无效的。处于invalid状态的cacheline是空的,没有数据。当新的数据要进入cache的时候,优选状态是invalid的cacheline,之所以如此是因为如果选中其他状态的cacheline,则说明需要替换cacheline数据,而未来如果再次访问这个被替换掉的cacheline数据的时候将遇到开销非常大的cache miss。

//在MESI协议中,每个CPU都会监听总线(bus)上的其他CPU对每个Cache line的所有操作,因此该协议也称为监听(snoop)协议
通常情况下,CPU需要以下几个通信message即可:
Read消息:read message用来获取指定物理地址上的cacheline数据
Read Response消息:该消息携带了read message请求的数据。read response可能来自memory,也可能来自其他的cache。例如:如果一个cache有read message请求的数据并且该cacheline的状态是modified,那么该cache必须以read response回应这个read message,因为该cache中保存了最新的数据。
Invalidate消息:该命令用来将其他cpu cache中的数据设定为无效。该命令携带物理地址的参数,其他CPU cache在收到该命令后,必须进行匹配,发现自己的cacheline中有该物理地址的数据,那么就将其移除并用Invalidate Acknowledge回应。
Invalidate Acknowledge消息: 收到invalidate message的cpu cache,在移除了其cache line中的特定数据之后,必须发送invalidate acknowledge消息。
Read Invalidate消息: 该message中也包括了物理地址这个参数,以便说明其想要读取哪一个cacheline数据。此外,该message还同时有invalidate message的功效,即其他的cache在收到该命令后,移除自己cacheline中的数据。因此,Read Invalidate message实际上就是read+invalidate。发送Read Invalidate之后,cache期望收到一个read response以及多个invalidate acknowledge。
Writeback消息: 该message包括两个参数,一个是地址,另外一个是写回的数据。该消息用在modified状态的cacheline被驱逐出境(给其他数据腾出地方)的时候发出,该命名用来将最新的数据写回到memory(或者其他的CPU cache中)

##Store Buffer
在CPU和cache之间增加store buffer这个HW block。
那么cpu 0无需等待其他CPU的相应,只需要将要修改的内容放入store buffer,然后继续执行就OK了。当cache line完成了bus transaction,并更新了cache line的状态后,要修改的数据将从store buffer进入cache line。
这种设计叫做store forwarding,当CPU执行load操作的时候,不但要看cache,还有看store buffer是否有内容,如果store buffer有该数据,那么就采用store buffer中的值。store forwarding解决了CPU 0的cache line和store buffer间的数据一致性问题。
smp_mb内存屏障。保证多核CPU的store buffer内容一致,保证store buffer完全写入cache line之前,其他数据不能先更新cache line。

###为了保证MESI协议的正确性,CPU在需要发出某个变量的a的MESI协议消息的时候,需要检查invalidate queue中是否有该变量a的invalidate消息,如果有需要先出来完成这个invliadte消息后,才能发出正确的MESI协议消息。

#无论哪一种cpu都遵守下面的规则:
[1]、从CPU自己的视角看,它自己的memory order是服从program order的
[2]、从包含所有cpu的sharebility domain的角度看,所有cpu对一个共享变量的访问应该服从若干个全局存储顺序
[3]、memory barrier需要成对使用
[4]、memory barrier的操作是构建互斥锁原语的基石

创作不易, 小小的支持一下吧!

微信支付宝

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/697097.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

让GNSSRTK不再难【第一天】

第1讲 GNSS系统组成以及应用 北斗导航科普动画_哔哩哔哩_bilibili 1.1 GNSS系统 1.1.1 基本概念 全球卫星导航系统&#xff08;Global Navigation Satellite System, GNSS&#xff09;&#xff0c;是能在地球表面或近地空间的任何地点为用户提供全天候的三维坐标、速度以及…

redis常用设计模式

Redis常用的设计模式分为读&#xff0c;写&#xff0c;读写三种 一、概要说明 读操作 Read Through Pattern 读穿透 写操作 以Redis统一视图为准&#xff1a;先更新缓存&#xff0c;后更新数据库。 Write Through Pattern 直写模式&#xff08;首先将数据写入缓存&#xf…

C语言 | Leetcode C语言题解之第135题分发糖果

题目&#xff1a; 题解&#xff1a; int candy(int* ratings, int ratingsSize) {int ret 1;int inc 1, dec 0, pre 1;for (int i 1; i < ratingsSize; i) {if (ratings[i] > ratings[i - 1]) {dec 0;pre ratings[i] ratings[i - 1] ? 1 : pre 1;ret pre;inc…

表的设计与查询

目录 一、表的设计 1.第一范式&#xff08;一对一&#xff09; 定义&#xff1a; 示例&#xff1a; 2.第二范式&#xff08;一对多&#xff09; 定义&#xff1a; 要求&#xff1a; 示例&#xff1a; 3.第三范式&#xff08;多对多&#xff09; 定义&#xff1a; 要求…

MySQL中的数据库约束

目录 导读&#xff1a; 约束类型 1、not null&#xff08;不能为空&#xff09; 2、unique(唯一) 3、default(默认值约束) 4、primary key(唯一)与unique 相同点&#xff1a; 不同点&#xff1a; auto_increment&#xff1a; 5、foreign key(外键) 语法形式&#xff…

在WSL2下配置Pytorch(Linux+Anaconda+PyTorch-GPU)

之前使用过双系统、虚拟机的ubunutu 效果都不是很好&#xff0c;要么切换系统太麻烦&#xff0c;要么太卡顿 最近在尝试WSL子系统&#xff0c;没有想到还是有很多坑 记录一下配置的过程 一、NVIDIA显卡的安装 因为WSL和Windows的显卡驱动不能互通&#xff0c;所以在子系统上需…

在iPhone上恢复删除Safari历史记录的方法[2024]

您是否正在寻找恢复 iPhone 上已删除的 Safari 历史记录的最佳方法&#xff1f;好吧&#xff0c;这篇文章提供了 4 种在有/无备份的情况下恢复 iPhone 上已删除的 Safari 历史记录的最佳方法。现在按照分步指南进行操作。 iPhone 上的 Safari 历史记录会被永久删除吗&#xff1…

开源项目学习——vnote

一、介绍 vnote是一款免费且开源的markdown编辑器&#xff0c;用C开发&#xff0c;基于Qt框架&#xff0c;windows/linux/mac都能用。 二、编译 $ git clone --recursive https://github.com/vnotex/vnote.git $ cd vnote && mkdir build $ cd build $ cmake ../ $ …

国外媒体软文发稿-引时代潮流-助力跨国企业蓬勃发展

大舍传媒&#xff1a;开疆拓土&#xff0c;引领传媒新潮流 随着全球经济的一体化和信息技术的高速发展&#xff0c;跨国企业在国际市场上的竞争越来越激烈。这也给跨国企业带来了巨大的机遇和挑战。在这个时代背景下&#xff0c;大舍传媒凭借其独特的优势和创新的服务模式&…

代码随想录算法训练营第五十四 | ● 392.判断子序列 ● 115.不同的子序列

392.判断子序列 https://programmercarl.com/0392.%E5%88%A4%E6%96%AD%E5%AD%90%E5%BA%8F%E5%88%97.html class Solution { public:bool isSubsequence(string s, string t) {if(s.size()0 )return true;if(t.size()0)return false;vector<vector<int>> dp(s.size(…

GAN的入门理解

这一篇主要是关于生成对抗网络的模型笔记&#xff0c;有一些简单的证明和原理&#xff0c;是根据李宏毅老师的课程整理的&#xff0c;下面有链接。本篇文章主要就是梳理基础的概念和训练过程&#xff0c;如果有什么问题的话也可以指出的。 李宏毅老师的课程链接 1.概述 GAN是…

不能访问huggingface、与GPU配置

不能访问huggingface解决方法 如果是从 huggingface.co 下载模型&#xff0c;由于国内不能访问&#xff0c;所以建议先配置一下环境变量&#xff0c; 通过访问国内镜像站点 https://hf-mirror.com来下载模型。 &#xff08;1&#xff09;Linux系统设置环境变量&#xff1a; e…

[Cloud Networking] Layer3 (Continue)

文章目录 1. DHCP Protocol1.1 DHCP 三种分配方式1.2 DHCP Relay (中继) 2. 路由协议 (Routing Protocol)2.1 RIP (Routing Information Protocol)2.2 OSPF Protocol2.3 BGP Protocol2.4 IS-IS Protocol2.5 ICMP&#xff08;Internet Control Message Protocol&#xff09; 1. …

Unity 设置默认字体(支持老版及新版TMP)

普通UI-Text设置 &#xff08;同一unity版本设置一次即可&#xff09; 1.首先工程的Resources目录下创建Fonts文件夹用于存放字体 如下图所示 2.找到Unity的安装目录下的Editor\Data\Resources\PackageManager\BuiltInPackages\com.unity.ugui\Runtime\UI\Core\Text.cs文件 …

YOLOv5改进 | 主干网络 | 用SimRepCSP作为主干网络提取特征【全网独家 + 降本增效】

&#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; SimRepCSP 类似于 YOLOv7的主干网络&#xff0c;由卷积模块和重参数化卷积&#xff08;RepConv&#xff09;模块组合而成&#xff0c;以 Cro…

特种设备起重机指挥题库附答案

1、【多选题】力的三要素是指:( )。(ACD) A、力的大小 B、力的单位 C、力的方向 D、力的作用点 2、【多选题】司索作业规范正确的要求是( )(ABC) A、吊点正确 B、吊索挂设合理 C、绑扎牢靠 D、吊索长短一致 3、【多选题】圆柱形物体兜吊时&#xff0c;一定要放空圈&#…

从零开始,手把手教你文旅产业策划全攻略

如果你想深入了解文旅策划的世界&#xff0c;那么有很多途径可以获取知识和灵感。 首先&#xff0c;阅读一些专业书籍也是一个不错的选择。书店或图书馆里有许多关于文旅策划的书籍&#xff0c;它们通常涵盖了策划的基本理论、方法和实践案例。通过阅读这些书籍&#xff0c;你…

python项目(豆瓣电影)

目录 1、项目效果 2、项目源码 3、技术实现 4、总结 前言 我的这个项目是做的一个豆瓣电影爬取&#xff0c;爬取了豆瓣电影的TOP排行榜的数据 包括电影的名称 演员 评分 评价人数等等 运用了TK布局助手 布了4个界面 有登录 注册 首页 详情 注意&#xff1a;项目并没有连接数…

讨论C++类与对象

讨论C类与对象 C语言结构体和C类的对比类的实例化类对象的大小猜想一猜想二针对上述猜想的实践 this指针不同对象调用成员函数 类的6个默认成员函数构造函数析构函数拷贝构造函数浅拷贝和深拷贝 赋值运算符重载 初始化列表初始化顺序 C语言结构体和C类的对比 在C语言中&#x…

深入探讨跨域请求(CORS):原理、解决方案与详细示例代码

深入探讨跨域请求&#xff08;CORS&#xff09;&#xff1a;原理、解决方案与详细示例代码 &#x1f310; 深入探讨跨域请求&#xff08;CORS&#xff09;&#xff1a;原理、解决方案与详细示例代码 &#x1f310;摘要引言正文内容什么是跨域&#xff1f;为什么会有跨域问题&am…