处理器协同机制其二内存屏障与内存顺序(及Store Buffer与Invalidate Queue)

3 篇文章 0 订阅
3 篇文章 0 订阅

 目录

处理器协同机制其一缓存一致性协议(MESI)

处理器协同机制其二内存屏障与内存顺序(及Store Buffer与Invalidate Queue)

处理器协同机制其三C++内存顺序与栅栏(及依赖性读屏障)

三、存储缓冲与无效队列

       当处理器需要的数据不在其缓存中,需要请求,等待内存或其他缓存来响应,这个过程降低了处理器的工作效率。为此,引入存储缓冲,处理器只需要将修改的内容放入存储缓冲,就可以继续执行了,存储缓冲中的数据会适时地刷新到其缓存中。

       存储缓冲的引入,可能会出现数据不一致的情况,为此需要存储转發机制。即处理器读取数据优先从存储缓冲中读取。

       同时,存储缓冲引入了存储的乱序,处理器先向存储缓冲写入数据,後向其缓存写入数据,对于另一处理器可能会感知写入顺序正好相反。为了保证写入顺序,应该在其间加入一个指令以标记存储缓冲(有标记的数据,接下来处理器不能直接写入缓存),这样的指令就是写屏障。

       引入存储缓冲并不能很好的提高性能,因为存储缓冲容易填满而导致效率降低,主要是因为“使无效消息”的处理速度跟不上。为此,引入无效队列,只要将“使无效消息”放入无效队列就可以回应“认可使无效消息”,在合适的时机处理无效队列中的消息。

       于是,处理器的缓存体系结构为:

       当然,为了保证一致性,处理器要将發送MESI消息,若无效队列中有相关缓存行的消息则必须等待之後才能进行。这个承诺,在对缓存行的竞争不是很严重的情况下能够成立。

       无效队列的引入,也引入了加载的乱序,另一处理器依序修改两个缓存行,某一处理器对“使无效消息”的处理可能是:先收到的“使无效消息”放入无效队列,後收到的直接处理或者没有收到该消息(因为此缓存行被对方独占)。这样一来,该处理器对缓存行的修改顺序的感知可能正好是相反的。为了保证缓存一致性的结果,若无效队列中有相关消息则CPU对缓存行的访问应该等待之後进行,这样也就保证了加载的结果有序,但不保证执行有序。

       同样地,处理器發现之後的加载可以直接进行(缓存了有效数据),之前的加载需要等待(需要从其他地方获取),于是加载的执行乱序了。设想,另一CPU先修改S状态缓存行再修改E/M状态缓存行,某CPU由于乱序执行先获取了未修改的S状态缓存行数据再获取传递过来的已修改的另一缓存行,很明显程序逻辑可能是相反的。

     读屏障防止上述加载的执行乱序,同时保证加载的结果有序(此为依赖性读屏障的功能)。

四、内存屏障与内存顺序

       现代处理器是乱序执行架构,其必须遵守以下规则:(乱序执行原则)

1、每个CPU都认为自己的内存访问按编程顺序进行。

2、仅当两个操作涉及不同的位置时,CPU才会对其存储操作重新排序。

3、某CPU的在读屏障之前的加载先于其任何在读屏障之後的加载被所有CPU所感知。

4、某CPU的在写屏障之前的存储先于其任何在写屏障之後的存储被所有CPU所感知。

5、某CPU在完全屏障之前的内存访问先于其任何在完全屏障之後的访问被所有CPU感知。

内存屏障保证的可见性不能在三个及以上CPU中传递,这种“传递性”需要另一种机制来保证,通常硬件设计者要考虑这种问题。目前所知道的CPU都提供“传递性”。

       内存访问重排序的类型:

1、读写重排序

LoadLoad barrier: 取取屏障,阻止其前的加载被重排序到其後的加载之後。

StoreStore barrier: 存存屏障,阻止其前的存储被重排序到其後的存储之後。

LoadStore barrier: 取存屏障,阻止其前的加载被重排序到其後的存储之後。

StoreLoad barrier: 存取屏障,阻止其前的存储被重排序到其後的加载之後。

2、原子操作重排序

Atomic with Loads: 原子指令与加载重排序。

Atomic with Stores: 原子指令与存储重排序。

3、依赖性加载重排序

smp_read_barrier_depends()是依赖性读屏障,当接下来的加载地址需要依赖之前加载的数据计算而得时,就存在数据依赖性。这种屏障在Alpha架构的CPU上存在。

4、指令缓存与指令流水线不一致

这种CPU需要为自修改代码执行特殊指令以保证一致性,例如:条件跳转指令。

带括号的CPU架构表示其支持该模式,但实际很少使用。

Linux内核的内存屏障:

1、smp_mb(): 完全屏障,防止读写重排序,用作“存取屏障”。

2、smp_rmb(): 读屏障,“取取屏障”的语义,同时用作“取存屏障”。

3、smp_wmb(): 写屏障,“存存屏障”的语义。

4、smp_read_barrier_depends(): 依赖性读屏障,通常可用读屏障代替。

5、mmiowb(): MMIO写屏障,用于保证被自旋锁保护的MMIO写入顺序,在某些平台上,自旋锁中的内存屏障已经保证了MMIO的顺序故而此屏障定义为空。这个原语比较新,故而较少驱动使用它。

以上这些原语(在指定平台上不为空)同时阻止了编译器为了优化而对内存访问重排序。同样,在单处理器上也有类似的内存屏障,这通常用于编写驱动。通常,可以不直接使用内存屏障,使用一些互斥原语(自旋锁、互斥锁、信号量等),其隐含了所需的内存屏障。

1、Alpha,独有的依赖性读屏障。

2、AMD64,与x86兼容,强一致性内存模型,某些SSE等指令使用较弱的内存模型,其读屏障指令是lfence,写屏障是sfence,完全屏障是mfence,一般只使用mfence就行。

3、x86,CPU提供处理顺序,因此存储不会乱序,但是,CPU通常不保证加载的顺序,故而加载可以在存储完成前提前执行。x86处理器不一定支持内存屏障(lfence,sfence,mfence),可能用带lock的指令代替之。同时,执行自修改代码之前需要特殊指令(例如跳转指令)。

4、其他架构,此处省略。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值