内存围栏降低了所有CPU核心的速度

Do memory fences slow down all CPU cores?

本文关键字:CPU 核心 速度 内存      更新时间:2023-10-16

在某个地方,有一次我读到关于内存栅栏(barriers)的文章。据说内存围栏会导致多个CPU内核之间的缓存同步。

所以我的问题是:

  1. 操作系统(或CPU本身)如何知道哪些内核需要同步?

  2. 它是否同步所有CPU核心的缓存?

  3. 如果(2)的答案是"是",并且假设同步操作并不便宜,那么使用内存围栏是否会减慢我的应用程序未使用的内核的速度?例如,如果我有一个单线程应用程序在我的8核CPU上运行,它会减慢CPU的所有其他7核的速度吗,因为一些缓存线必须与所有这些核同步?

  4. 上面的问题完全是无知的,围栏的工作方式完全不同吗?

  1. 操作系统不需要知道,每个CPU核心都会按照指令执行:每个有内存围栏的核心都必须在之前或之后执行某些操作,仅此而已。一个核心不是在与其他核心同步,而是在同步相对于自身的内存访问
  2. 一个核心中的围栏并不意味着其他核心与之同步,因此通常会有两个(或多个)围栏:一个在编写器中,一个在读取器中。在一个核心上执行的围栏不需要影响任何其他核心。当然,一般来说,这并不能保证,只是希望理智的体系结构不会过度地串行化多核执行

通常,内存栅栏用于排序本地操作。以这个伪汇编代码为例:

load A
load B

许多CPU不能保证B确实在A之后被加载,B可能由于某些其他内存加载而在较早加载到缓存中的缓存行中。如果你引入围栏,

load A
readFence
load B

你可以保证B是在A之后从内存加载的。如果B在缓存中,但比A早,它就会被重新加载。

商店的情况也是如此。带

store A
store B

一些CPU可能决定在写入A之前将B写入内存。同样,可能需要在两条指令之间设置一道屏障来强制执行操作的顺序。是否需要内存围栏始终取决于体系结构。


通常,您成对使用内存栅栏:

  • 如果一个线程想要发布一个对象,它首先构造该对象,然后在将指向该对象的指针写入公共位置之前执行写围栏。

  • 想要接收对象的线程从公开的内存位置读取指针,然后执行读取围栏,以确保基于该指针的所有进一步读取都能实际给出发布线程想要的值。

如果缺少任一围栏,则读取器可能在初始化对象之前读取对象的一个或多个数据成员的值。疯狂随之而来。

如果你有八个核心,每个核心都在做不同的事情,那么这些核心就不会访问相同的内存,也不会在缓存行中拥有相同的内存。

如果核心#1使用内存围栏,但没有其他核心访问核心#1访问的内存,那么其他核心根本不会减慢速度。然而,如果核心#1写入位置X,使用内存围栏,那么核心#2尝试读取相同的位置X,内存围栏将确保核心#2丢弃位置X的值(如果它在高速缓存中),并从RAM读取回数据,从而获得与核心#1写入的数据相同的数据。这当然需要时间,但这正是记忆围栏的作用所在。

(如果内核共享一些缓存,则数据将从缓存中读取,而不是从RAM中读取。)