国色天香在线观看全集免费播放

你的位置:国色天香在线观看全集免费播放 > 无码精品久久久天天影视 > 无码精品久久久天天影视

字节面:什么是伪分享?

发布日期:2022-06-18 17:03    点击次数:145

字节面:什么是伪分享?

 

本文转载自微信公众号「小林coding」,作家小林coding 。转载本文请联系小林coding公众号。

内行好,我是小林。

周末的时候,有个读者跟我说,口试字节的时候被问到:「什么是伪分享?又该若何幸免伪分享的问题?」

这个其实是熟悉 CPU 缓存的问题,我之前的图解系统也有提到过。

今天,我再跟内行讲一下。

正文 CPU 如何读写数据的?

先来坚毅 CPU 的架构,惟一贯通了 CPU 的 架构,才能更好地贯通 CPU 是如何读写数据的,关于当代 CPU 的架构图如下:

不错看到,一个 CPU 里普通会有多个 CPU 中枢,比如上图中的 1 号和 2 号 CPU 中枢,况兼每个 CPU 中枢都有我方的 L1 Cache 和 L2 Cache,而 L1 Cache 普通分为 dCache(数据缓存) 和 iCache(教唆缓存),L3 Cache 则是多个中枢分享的,这即是 CPU 典型的缓归档次。

上头提到的都是 CPU 里面的 Cache,放眼外部的话,还会有内存和硬盘,这些存储拓荒共同组成了金字塔存储档次。如下图所示:

从上图也不错看到,从上往下,存储拓荒的容量会越大,而访谒速率会越慢。至于每个存储拓荒的访谒延时,你不错看下图的表格:

你不错看到, CPU 访谒 L1 Cache 速率比访谒内存快 100 倍,这即是为什么 CPU 里会有 L1~L3 Cache 的原因,诡计即是把 Cache 行为 CPU 与内存之间的缓存层,以减少对内存的访谒频率。

CPU 从内存中读取数据到 Cache 的时候,并不是一个字节一个字节读取,而是一块一块的形状来读取数据的,这一块一块的数据被称为 CPU Line(缓存行),是以 CPU Line 是 CPU 从内存读取数据到 Cache 的单元。

至于 CPU Line 大小,在 Linux 系统不错用底下的形状检察到,你不错看我就业器的 L1 Cache Line 大小是 64 字节,也就意味着 L1 Cache 一次载入数据的大小是 64 字节。

那么对数组的加载, CPU 就会加载数组里面结合的多个数据到 Cache 里,因此咱们应该按照物理内存地址溜达的律例去访谒元素,这么访谒数组元素的时候,Cache 射中率就会很高,于是就能减少从内存读取数据的频率, 从而可提高圭臬的性能。

然而,在咱们不使用数组,而是使用单独的变量的时候,则会有 Cache 伪分享的问题,Cache 伪分享问题上是一个性能杀手,咱们应该要逃匿它。

接下来,就来望望 Cache 伪分享是什么?又如何幸免这个问题?

当今假定有一个双中枢的 CPU,这两个 CPU 中枢并交运行着两个不同的线程,它们同期从内存中读取两个不同的数据,区别是类型为 long 的变量 A 和 B, 欧美在线视频这个两个数据的地址在物理内存上是结合的,如若 Cahce Line 的大小是 64 字节,况兼变量 A 在 Cahce Line 的开首位置,那么这两个数据是位于吞并个 Cache Line 中,又因为 CPU Line 是 CPU 从内存读取数据到 Cache 的单元,是以这两个数据会被同期读入到了两个 CPU 中枢中各自 Cache 中。

咱们来思考一个问题,如若这两个不同中枢的线程区别修改不同的数据,比如 1 号 CPU 中枢的线程只修改了 变量 A,或 2 号 CPU 中枢的线程的线程只修改了变量 B,会发生什么呢?

分析伪分享的问题

当今咱们采集保证多核缓存一致的 MESI 公约,来诠释这一统共这个词的经由,如若你还不解析 MESI 公约,你不错看我这篇著作「10 张图翻开 CPU 缓存一致性的大门」。

①. 最启动变量 A 和 B 都还不在 Cache 里面,假定 1 号中枢绑定了线程 A,2 号中枢绑定了线程 B,线程 A 只会读写变量 A,线程 B 只会读写变量 B。

②. 1 号中枢读取变量 A,由于 CPU 从内存读取数据到 Cache 的单元是 Cache Line,也恰巧变量 A 和 变量 B 的数据包摄于吞并个 Cache Line,是以 A 和 B 的数据都会被加载到 Cache,并将此 Cache Line 标志为「独占」情景。

③. 接着,2 号中枢启动从内存里读取变量 B,相通的亦然读取 Cache Line 大小的数据到 Cache 中,此 Cache Line 中的数据也包含了变量 A 和 变量 B,无码精品久久久天天影视此时 1 号和 2 号中枢的 Cache Line 情景变为「分享」情景。

④. 1 号中枢需要修调动量 A,发现此 Cache Line 的情景是「分享」情景,是以先需要通过总线发送音问给 2 号中枢,奉告 2 号中枢把 Cache 中对应的 Cache Line 标志为「已失效」情景,然后 1 号中枢对应的 Cache Line 情景造成「已修改」情景,况兼修调动量 A。

⑤. 之后,2 号中枢需要修调动量 B,此时 2 号中枢的 Cache 中对应的 Cache Line 是已失效情景,另外由于 1 号中枢的 Cache 也有此相通的数据,且情景为「已修改」情景,是以要先把 1 号中枢的 Cache 对应的 Cache Line 写回到内存,然后 2 号中枢再从内存读取 Cache Line 大小的数据到 Cache 中,临了把变量 B 修改到 2 号中枢的 Cache 中,并将情景标志为「已修改」情景。

是以,不错发现如若 1 号和 2 号 CPU 中枢这么连续轮流的区别修调动量 A 和 B,就会类似 ④ 和 ⑤ 这两个才能,Cache 并莫得起到缓存的遵守,诚然变量 A 和 B 之间其实并莫得任何的关系,然而因为同期包摄于一个 Cache Line ,这个 Cache Line 中的率性数据被修改后,都会互相影响,从而出现 ④ 和 ⑤ 这两个才能。

因此,这种因为多个线程同期读写吞并个 Cache Line 的不同变量时,而导致 CPU Cache 失效的气候称为伪分享(False Sharing)。

幸免伪分享的递次

因此,关于多个线程分享的热门数据,即普通会修改的数据,应该幸免这些数据刚好在吞并个 Cache Line 中,不然就会出现为伪分享的问题。

接下来,望望在本体技俩中是用什么形状来幸免伪分享的问题的。

在 Linux 内核中存在 __cacheline_aligned_in_smp 宏界说,是用于料理伪分享的问题。

从上头的宏界说,咱们不错看到:

如若在多核(MP)系统里,该宏界说是 __cacheline_aligned,也即是 Cache Line 的大小; 而如若在单核系统里,该宏界说是空的;

因此,针对在吞并个 Cache Line 中的分享的数据,如若在多核之间竞争相比严重,为了扎眼伪分享气候的发生,不错接纳上头的宏界说使得变量在 Cache Line 里是对齐的。

举个例子,有底下这个结构体:

结构体里的两个成员变量 a 和 b 在物理内存地址上是结合的,于是它们可能会位于吞并个 Cache Line 中,如下图:

是以,为了扎目前边提到的 Cache 伪分享问题,咱们不错使用上头先容的宏界说,将 b 的地址竖立为 Cache Line 对齐地址,如下:

这么 a 和 b 变量就不会在吞并个 Cache Line 中了,如下图:

是以,幸免 Cache 伪分享本体上是用空间换技能的思惟,挥霍一部分 Cache 空间,从而换来性能的进步。

咱们再来看一个哄骗层面的逃匿决议,有一个 Java 并发框架 Disruptor 使用「字节填充 + 接管」的形状,来幸免伪分享的问题。

Disruptor 中有一个 RingBuffer 类和会俗被多个线程使用,代码如下:

你可能会以为 RingBufferPad 类里 7 个 long 类型的名字很奇怪,但事实上,它们诚然看起来毫无作用,但却对性能的进步起到了至关热切的作用。

咱们都解析,CPU Cache 从内存读取数据的单元是 CPU Line,一般 64 位 CPU 的 CPU Line 的大小是 64 个字节,一个 long 类型的数据是 8 个字节,是以 CPU 一下会加载 8 个 long 类型的数据。

把柄 JVM 对象接管关系中父类成员和子类成员,内存地址是结合摆设布局的,因此 RingBufferPad 中的 7 个 long 类型数据行为 Cache Line 前置填充,而 RingBuffer 中的 7 个 long 类型数据则行为 Cache Line 后置填充,这 14 个 long 变量莫得任何本体用途,更不会对它们进行读写操作。

另外,RingBufferFelds 里面界说的这些变量都是 final 修饰的,意味着第一次加载之后不会再修改, 又由于「前后」各填充了 7 个不会被读写的 long 类型变量,是以无论若何加载 Cache Line,这统共这个词 Cache Line 里都莫得会发生更新操作的数据,于是只须数据被频繁地读取访谒,就当然没独特据被换出 Cache 的可能,也因此不会产生伪分享的问题。