zoukankan      html  css  js  c++  java
  • 学习load acquire 和store release

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

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

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

    interlocked_read.hpp

    [cpp:showcolumns] view plaincopyprint?
    ·········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 plaincopyprint?
    ·········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就够了。

  • 相关阅读:
    C/C++ string.h头文件小结
    linux根据进程pid查看进程详细信息
    《mysql必知必会》读书笔记
    安装vim with python
    vim正则表达式小结
    C语言指针篇(二)多级指针
    C语言指针篇(一)指针与指针变量
    递归函数
    lan口和wan口的配置
    C语言基础篇(三) 指针
  • 原文地址:https://www.cnblogs.com/xiayong123/p/3717133.html
Copyright © 2011-2022 走看看