学习load acquire 和store release

原创 2011年01月09日 21:42:00

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

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

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

interlocked_read.hpp

 

这就是许多牛人在讨论内存模型时提到的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在其它编译器下面的实现

 

 

 

 

 

这里用到了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的理解。可能从一开始都理解不对。如果各位高手有什么观点,可以和我联系chengyongxin1983@qq.com

 

[置顶] volatile、内存屏障、Acquire&Release语义 三者的差别和关系(二) —— 之内存屏障

这篇博客是系列文章的第二篇, 主要讲一下内存屏障, 不会讲的很深, 但求明确理解和记住, 什么是内存屏障! 1. 背景知识: CPU乱序执行 这个背景知识很重要, 先讲为快, 在wiki百...
  • peterlin666
  • peterlin666
  • 2014年06月04日 13:48
  • 635

Acquire and Release Semantics

SEP 13, 2012 http://preshing.com/20120913/acquire-and-release-semantics/ 通常说来,在lock-free编程中,线程之间有...
  • sparkliang
  • sparkliang
  • 2016年11月21日 15:34
  • 1504

python2.7 threading 模块 二 Lock RLock

Lock的概念很简单,就不多解释了 锁有两种状态:被锁(locked)和没有被锁(unlocked)。拥有acquire()和release()两种方法,并且遵循一下的规则:...
  • karchar
  • karchar
  • 2016年08月30日 23:13
  • 2721

ARMv8 Load / store 指令学习重要笔记

1、Load-Store Non-temporal Pair STNP Xt1, Xt2, [base,#imm] Store Non-temporal Pair (exten...
  • forever_2015
  • forever_2015
  • 2016年09月24日 21:09
  • 1814

学习load acquire 和store release

学习load acquire 和store release
  • forfuture3513
  • forfuture3513
  • 2016年08月16日 22:29
  • 257

深入理解CUDA线程层次以及关于设置线程数的思考

GPU线程以网格(grid)的方式组织,而每个网格中又包含若干个线程块,在G80/GT200系列中,每一个线程块最多可包含512个线程,Fermi架构中每个线程块支持高达1536个线程。同一线程块中的...
  • mydear_11000
  • mydear_11000
  • 2015年09月16日 08:45
  • 3423

ExtJs.Store.load()各参数含义解析

ExtJs的Store在加载时候一般是延迟加载的,这时候Grid就会先出现一片空白,等加载完成后才出现数据;因此,我们需要给它添加一个提示信息!;加载完毕关闭提示框 解决方案: 1.给store添...
  • gxftry1st
  • gxftry1st
  • 2013年12月18日 18:17
  • 22176

ext中store.load和store.reload的区别

store load()和reload() load( Object options ) : Boolean 采用配置好的Reader格式去加载Record缓存,具体请求的任务由配置好的P...
  • silence1214
  • silence1214
  • 2011年11月16日 13:31
  • 23799

学习笔记:acquire-release初步理解

#include #include #include #include #include #include #include using namespace std; atomic g...
  • audi2
  • audi2
  • 2014年09月26日 18:01
  • 322

Acquire and Release Semantics

Acquire and Release Semantics 1(共 3)对本文的评价是有帮助 评价此主题 An operation has acquire semantics...
  • summerhust
  • summerhust
  • 2012年03月29日 11:46
  • 905
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:学习load acquire 和store release
举报原因:
原因补充:

(最多只允许输入30个字)