内存屏障由来的理解和使用《编程高手必学的内存知识》学习笔记 Day 5

文章介绍了内存屏障的概念,以及其在解决多核CPU中由storebuffer和invalidqueue带来的缓存一致性问题上的作用。通过讲解释放和获取语义,展示了如何通过内存屏障来保证程序的顺序正确性。文章还提到了不同CPU架构如X86和ARM的处理方式,并给出了代码示例来说明内存屏障的重要性。
摘要由CSDN通过智能技术生成

系列文章目录

这是本周期内系列打卡文章的所有文章的目录



前言

提示:这里可以添加本文要记录的大概内容:

学习内容:https://time.geekbang.org/column/article/462113
知识主客体建立关系:
之前研究DPDK无锁队列,看到实现中有些读屏障和写屏障,只是模糊理解个大概,但不知道它在什么时候出问题,所有用屏障保证时序正确性。现在看了这篇文章,理解了读写屏障解决store buffer引入的问题,读屏障解决invalid queue问题,两个异步化设计提升性能之后,要兼容Cache机制协议,保证程序在多核上时序正确。


提示:以下是本篇文章正文内容,下面案例可供参考

一、概念理解(What)

这点文章解释得很清楚,我整理一下从文章中学到的概念。

  1. buffer和cache的区别:打个比喻,cache 往往意味着它所存储的信息是副本,但是 buffer 更像是蓄水池,buffer 中的数据没有副本,一旦丢失就彻底丢失了。两者都是结构性的优化,cache 存在的意义是加速查找
  2. store buffer 作为buffer结构引入的设计,是硬件实现的缓冲区,自然它的读写速度比缓存的速度更快,看下图就懂了。先搜集多次写操作,然后在合适的时机进行提交,作为CPU私有,同样需要兼容对应的缓存协议。

在这里插入图片描述

store buffer 的存在是为提升写性能,放弃了缓存的顺序一致性,我们把这种现象称为弱缓存一致性

  1. 内存屏障,屏障的作用是前边的读写操作未完成的情况下,后面的读写操作不能发生。如Arm 上 dmb 指令。

  2. 失效队列:

由于 store buffer 的存在提升了写入速度,那么 invalid 消息确认速度相比起来就慢了,这就带来了速度的不匹配,很容易导致 store buffer 的内容还没有及时更新到 cache 里,自己的容量就被撑爆了,从而失去了加速的作用。为了解决这个问题,CPU 设计者又引入了“invalid queue”

如下图:
在这里插入图片描述
5. 读写屏障分离
分离的写屏障和读屏障的出现,是为了更加精细地控制 store buffer 和 invalid queue 的顺序。

  1. 单向屏障

(half-way barrier) 也是一种内存屏障,但它并不是以读写来区分的,而是像单行道一样,只允许单向通行,例如 Arm 中的 stlr 和 ldar 指令就是这样。
stlr 的全称是 store release register,也就是以 release 语义将寄存器的值写入内存;ldar 的全称是 load acquire register,也就是以 acquire 语义从内存中将值加载入寄存器。我们重点就来看看 release 和 acquire 语义。

语义对比
release 语义挡前不挡后
acquire 语义挡后不挡前

二、内存屏障,解决方案发展的脉络(How、Why)

  1. 用 store buffer 也会有一个问题,那就是它并不能保证变量写入缓存和主存的顺序,这也就需要引入内存屏障。

一个代码示例,可以用GEM5 CPU仿真做成验证实验:


// CPU0
void foo() {
    a = 1;
    b = 1;
}

// CPU1
void bar() {
    while (b == 0) continue;
    assert(a == 1);
}

在这个代码块中,CPU0 执行 foo 函数,CPU1 执行 bar 函数。但在对变量 a 和 b
进行赋值的时候,有两种情况会导致它们的赋值顺序被打乱。

第一种情况是 CPU 的乱序执行。 CPU 为了提升运行效率和提高缓存命中率,采用了乱序执行。
第二种情况是 store buffer 在写入时,有可能 b 所对应的缓存行会先于 a
所对应的缓存行进入独占状态,也就是说 b 会先写入缓存。

  1. 由于 store buffer 的存在提升了写入速度,那么 invalid 消息确认速度相比起来就慢了,这就带来了速度的不匹配,很容易导致 store buffer 的内容还没有及时更新到 cache 里,自己的容量就被撑爆了,从而失去了加速的作用。为了解决这个问题,CPU 设计者又引入了**“invalid queue”**

极客时间版权所有: https://time.geekbang.org/column/article/462113
3. 分离的写屏障和读屏障的出现,是为了更加精细地控制 store buffer 和 invalid queue 的顺序

三、延伸学习

内存屏障是硬件篇,其中不同体系结构CPU的设计,都有各有特色的方案及取舍。可以延伸的点也很多:

  1. X86的模型:为啥X86 采用的 TSO 模型不存在缓存一致性的问题
  2. ARM的CPU设计:通过单向通行来区分屏障
  3. alpha体系结构:通过读写来区分屏障
  4. 语言层面,如Java的Unsafe对内存屏障的抽象和支持
  5. 从MESI延伸出弱缓存一致性协议,具体协议细节等
  6. 内存屏障生产方案的使用案例:DPDK无锁队列实现等

总结

提示:这里对文章进行总结:

内在脉络,软件开发随着硬件设计而发展:

CPU 从单核发展为多核,增加缓存,导致出现了多个核间的缓存一致性问题 --> 为了解决缓存一致性问题,提出了 MESI 协议 --> 完全遵守 MESI 又会给 CPU 带来性能问题 --> CPU 设计者又增加 store buffer 和 invalid queue --> 又导致了缓存的顺序一致性变为了弱缓存一致性 --> 需要缓存的顺序一致性的,就需要软件工程师自己在合适的地方添加内存屏障。

内容来源:
极客时间:16 | 编程高手必学的内存知识

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值