中文
注册
我要评分
文档获取效率
文档正确性
内容完整性
文档易理解
在线提单
论坛求助
鲲鹏小智

简介

什么是内存屏障

由于现代CPU的运行速度往往要比内存要快得多——一般在从内存获取一个变量的同时,CPU可以执行数百条指令,因此现代计算机架构往往在CPU和内存之间增加一级缓存,以允许在缓存中快速访问较为频繁使用的数据。与此同时,CPU被设计成在从内存中获取数据的同时,可以执行其他指令和内存引用,这就导致了指令和内存引用的乱序执行。为了解决这一内存乱序问题,引入了各种同步原语,这些原语通过使用内存屏障来实现多处理器之间内存访问的顺序一致性。

本章以C/C++语言为例阐述内屏屏障基本原理和使用方法,Java语言已使用JDK对屏障原语进行了适配,代码调用及相关知识可参考Java同步原语

什么时候需要使用内存屏障

仅仅在两个CPU之间存在需要通过共享内存来实现交互的可能时,才需要使用内存屏障。

使用原始的内存屏障原语不是最好的选择,建议使用包含内存屏障语义的高级原语。例如:可以使用C++原子操作的内存屏障接口,详情可参考https://en.cppreference.com/w/cpp/atomic/memory_order

什么时候不需要显式使用内存屏障

各种架构下内存序模型定义有所差异,下面介绍在ARM架构下,不需要显式使用内存屏障的几个典型场景。

  • 存在地址依赖时,不需要显式使用内存屏障,也可保证内存一致性。
    例如:
    LDR X1,[X2] 
    AND X1, X1, XZR 
    LDR X4,[X3, X1]

    第三行的汇编语句从内存地址[X3, X1]读数据到寄存器X4时,需要依赖于寄存器X1从地址[X2]中读到的值,所以在这种情况下,LDR X1, [X2]将先于LDR X4, [X3, X1]执行。

  • 存在控制依赖时,不需要显式使用内存屏障,也可保证内存一致性。

    例如:

    r1 = x;
    if(r1 ==0) nop(); 
    y =1;

    如果一个条件分支依赖于一个加载操作,那么在条件分支后面的存储操作都在加载操作后执行。所以r1 = x将先于y = 1执行。

  • 存在寄存器数据依赖时,不需要显式使用内存屏障,也可保证内存一致性。

    例如:

    LDR X1,[X2] 
    ADD X3, X3, X1 
    SUB X3, X3, X1 
    STR X4,[X3]

    以上的语句执行过程中,在执行最后的STR指令时依赖于X3寄存器中存放的内存地址,而X3寄存器的值又依赖于从[X2]内存地址中获取到的数据X1寄存器的值;这些寄存器之间存在数据依赖,所以LDR X1, [X2]将先于STR X4, [X3]执行。