学习load acquire 和store release

2024-02-13 06:48

本文主要是介绍学习load acquire 和store release,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这个问题困扰了我很久,一直都想不明白。今天好像有点通了,立即记录下来。仅是个人理解。

在学习BOOST多线程库的原码时

这样一个头文件引起了我的注意:

interlocked_read.hpp

[cpp:showcolumns] view plain copy print ?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #ifdef BOOST_MSVC   
  2. extern "C" void _ReadWriteBarrier(void);  
  3. #pragma intrinsic(_ReadWriteBarrier)   
  4. namespace boost  
  5. {  
  6.     namespace detail  
  7.     {  
  8.         inline long interlocked_read_acquire(long volatile* x)  
  9.         {  
  10.             long const res=*x;  
  11.             _ReadWriteBarrier();  
  12.             return res;  
  13.         }  
  14.         inline void* interlocked_read_acquire(voidvolatile* x)  
  15.         {  
  16.             voidconst res=*x;  
  17.             _ReadWriteBarrier();  
  18.             return res;  
  19.         }  
  20.         inline void interlocked_write_release(long volatile* x,long value)  
  21.         {  
  22.             _ReadWriteBarrier();  
  23.             *x=value;  
  24.         }  
  25.         inline void interlocked_write_release(voidvolatile* x,void* value)  
  26.         {  
  27.             _ReadWriteBarrier();  
  28.             *x=value;  
  29.         }  
  30.     }  
  31. }  
  32. #else   
  33. namespace boost  
  34. {  
  35.     namespace detail  
  36.     {  
  37.         inline long interlocked_read_acquire(long volatile* x)  
  38.         {  
  39.             return BOOST_INTERLOCKED_COMPARE_EXCHANGE(x,0,0);  
  40.         }  
  41.         inline void* interlocked_read_acquire(voidvolatile* x)  
  42.         {  
  43.             return BOOST_INTERLOCKED_COMPARE_EXCHANGE_POINTER(x,0,0);  
  44.         }  
  45.         inline void interlocked_write_release(long volatile* x,long value)  
  46.         {  
  47.             BOOST_INTERLOCKED_EXCHANGE(x,value);  
  48.         }  
  49.         inline void interlocked_write_release(voidvolatile* x,void* value)  
  50.         {  
  51.             BOOST_INTERLOCKED_EXCHANGE_POINTER(x,value);  
  52.         }  
  53.     }  
  54. }  
  55. #endif  
 

这就是许多牛人在讨论内存模型时提到的load acquire 和store release的实现。

关于load acquire 和store release,在博客园上有两位大牛的文章说得很明白。

http://www.cnblogs.com/lxconan/archive/2008/07/19/1246776.html

http://www.cnblogs.com/sun/archive/2010/02/03/1663064.html#2001829

从这两篇文章,我学到了两方面知识:

1.有几种情况可能影响代码的顺序(这里的顺序可能不仅是执行顺序,还包括外部的感知顺序)

    a.编译器对代码的优化,导致编译出的代码顺序与程序员写出来的源代码顺序不一致。

    b.CPU进行指令的reorder,CPU可以在执行时将指令顺序打乱。导致实际执行顺序与编译后代码的顺序不一致。

    c.CPU写内存时,由于store buffer的存在,导致外部对写内存动作的感知的延迟

    b.store buffer的flush操作可能是以低地址到高地址顺序进行的,所以导致外部对写内存的动作感知顺序不一致


2.强内存模型下,存在store buffer,但外部对store buffer写内存动作的感知是有序的。弱内存模型下外部对store buffer写内存动作的感知可能是无序的。X86-64,AMD64的内存模型都是强内存模型,而IA64的内存模型是弱内存模型(比如安腾)。


 

有了这些基础知识,现在来理解load acquire 和store release就比较容易了。

不过上面两篇文章是基于C#的,没有一篇关于C++的load acquire 和store release。所以既然boost实作出来了,我很想研究一下,load acquire 和store release和普通的load store有什么差别。

 


我用VC2005编译了这段代码, 我机器的CPU是Intel(R) Core(TM) Duo


        inline long interlocked_read_acquire(long volatile* x)

        {

004073B0  push        ebp  

004073B1  mov         ebp,esp 

004073B3  push        ecx  

            long const res=*x;

004073B4  mov         eax,dword ptr [x] 

004073B7  mov         ecx,dword ptr [eax] 

004073B9  mov         dword ptr [res],ecx 

            _ReadWriteBarrier();

            return res;

004073BC  mov         eax,dword ptr [res] 

        }

004073BF  mov         esp,ebp 

004073C1  pop         ebp  

004073C2  ret   


发现interlocked_read_acquire生成的汇编代码出奇的简单,没有什么特别的,连我期待的mfence之类的代码都没有出现。那么interlocked_read_acquire有什么特别的呢?

从源代码开始分析。源代码添加了volatile关键字,volatile告诉编译器不要尝试优化对变量的存取,但这似乎没有对代码顺序作出任何的限定。那么是_ReadWriteBarrier()又是干什么的?它似乎被编译器忽略了。查了MSDN才知道,VC的编译器可能对volatile关键字进行reorder,_ReadWriteBarrier只是告诉编译器禁止这种优化,它并不是真正意义的内存栅栏,而是相当于一句编译命令。那么这里没有一句代码有内存栅栏的功能,如何实现load acquire呢?

下面这篇文章解释了这个问题

http://blogs.msdn.com/b/kangsu/archive/2007/07/16/volatile-acquire-release-memory-fences-and-vc2005.aspx

文章指出,在强内存模型下,volatile关键字声明的变量已经具备load acquire和store release的功能了。在弱内存模型下,才需要用fence指令。因此,vc2005为x86-64以及AMD64平台编译的代码,只要加入了volatile关键字,就一定是具备load acquire和store release的功能。

微软MSDN有对vc2005提供的volatile关键字作出了以下的解释

A write to a volatile object (volatile write) has Release semantics

A read of a volatile object (volatile read) has Acquire semantics

意思就是告诉程序员,放心地把volatile当作可以load acquire和store release的关键字来使用。那么,在X86-64平台下,用volatile不会增加任何额外代码,在IA64平台下,用volatile会增加内存栅栏的指令。分析到此,已经可以明白interlocked_read_acquire在VC2005下的实现是完全成确的了。

 

那么再来看interlocked_read_acquire在其它编译器下面的实现

 

[cpp:showcolumns] view plain copy print ?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. inline long interlocked_read_acquire(long volatile* x)  
  2. {  
  3.     return BOOST_INTERLOCKED_COMPARE_EXCHANGE(x,0,0);  
  4. }  
 

 

 

 

这里用到了lock指令,lock指令也具有栅栏的功能,因此,这里的实现是没有问题的。


另外,关于以下问题

 

 

 

    P0          P1
    ==========  ==========
    X = 1;      Y = 1;
    R0 = X;     R2 = Y;
    R1 = Y;     R3 = X;

 

问:如果X, Y 为 volatile 有没有可能使得执行完毕之后 R1 == R3 == 0 呢?


有的看法是,这个例子在弱内存模型下会出现,在强内存模型下不会出现,但我的理解是弱内存模型和强内存模型下都会出现,因为出现 R1 == R3 == 0 

的原因在于store buffer,而强弱两种内存模型都有store buffer(除非连缓存都没有的CPU),因此,这种出现这种情况不能说明是哪种内存模型。


再次,关于volatile无用论,我觉得或许绝对了。如果你想写一些可以在AMD,IA64,POWERPC等什么平台上都可以运行的代码,那么volatile可能是没有用了,取代它的是用load acquire和store release的操作。如果你确定你的代码运行在强内存模型下,那么volatile就够了。

这篇关于学习load acquire 和store release的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件