LiFengMing LiFengMing
首页
云原生
中间件
工具导航
资源导航
  • 分类
  • 标签
  • 归档
关于作者
GitHub (opens new window)

LiFengMing

IT届李哥
首页
云原生
中间件
工具导航
资源导航
  • 分类
  • 标签
  • 归档
关于作者
GitHub (opens new window)
  • 编程语言

    • Go

    • Java

      • 高性能无锁队列简介
      • 漫谈内存伪共享
        • 什么是伪共享
          • 什么是缓存行
          • 伪共享如何产生的
          • 缓存一致性协议MESI
          • 如何规避伪共享
      • 漫谈JVM内存屏障
      • 漫谈JDK原生并发队列
      • 神奇的高性能无锁队列Disruptor
      • 神奇的高性能无锁队列JCTools
      • Linux内核之kfifo
  • 问题排查手册

  • 容器编排技术

  • 云原生
  • 编程语言
  • Java
LiFengMing
2020-08-20
目录

漫谈内存伪共享

# 什么是伪共享

在计算机组成中,CPU 的运算速度比内存高出几个数量级,为了 CPU 能够更高效地与内存进行交互,在 CPU 和内存之间设计了多层缓存机制,如下图所示。

image-20210827230748974

一般来说,CPU 会分为三级缓存,分别为L1 一级缓存、L2 二级缓存和L3 三级缓存。越靠近 CPU 的缓存,速度越快,但是缓存的容量也越小。所以从性能上来说,L1 > L2 > L3,容量方面 L1 < L2 < L3。

CPU 读取数据时,首先会从 L1 查找,如果未命中则继续查找 L2,如果还未能命中则继续查找 L3,最后还没命中的话只能从内存中查找,读取完成后再将数据逐级放入缓存中。此外,多线程之间共享一份数据的时候,需要其中一个线程将数据写回主存,其他线程访问主存数据。

由此可见,引入多级缓存是为了能够让 CPU 利用率最大化。如果你在做频繁的 CPU 运算时,需要尽可能将数据保持在缓存中。

# 什么是缓存行

Cache Line 是 CPU 缓存可操作的最小单位,CPU 缓存由若干个 Cache Line 组成。Cache Line 的大小与 CPU 架构有关,在目前主流的 64 位架构下,Cache Line 的大小通常为 64 Byte。Java 中一个 long 类型是 8 Byte,所以一个 Cache Line 可以存储 8 个 long 类型变量。CPU 在加载内存数据时,会将相邻的数据一同读取到 Cache Line 中,因为相邻的数据未来被访问的可能性最大,导致缓存好行失效,这样导致 CPU 频繁与内存进行交互。

查看缓存行命令:
Mac:sysctl machdep.cpu.cache.linesize

Linux:getconf -a|grep CACHE  或者 cat /proc/cpuinfo |grep -I cache
1
2
3
4

# 伪共享如何产生的

  1. 假设变量 x,y 被加载到同一个 Cache Line,它们会被高频地修改。
  2. 当线程 1 在 CPU Core1 中对变量 x 进行修改,修改完成后 CPU Core1 会通知其他 CPU Core 该缓存行已经失效。
  3. 然后线程 2 在 CPU Core2 中对变量 y 进行修改时,发现 Cache line 已经失效,此时 CPU Core1 会将数据重新写回内存,CPU Core2 再从内存中读取数据加载到当前 Cache line 中。

# 缓存一致性协议MESI (opens new window)

每个处理器都有自己的高速缓存,而又共享同一主内存。当多个处理器都涉及同一块主内存区域的更改时,将导致各自的的缓存数据不一致。那同步到主内存时该以谁的缓存数据为准呢?

为了解决一致性的问题,需要各个处理器访问缓存时都遵循一些协议,在读写时要根据协议来进行操作,来保证处理器间缓存的一致性。这类协议有MSI、MESI、MOSI等。

MESI是Modified(修改)、Exclusive(独占)、Shared(共享)、Invaild(失效)四种状态的缩写,是用来修饰缓存行的状态。在每个缓存行前额外使用2bit,来表示此四种状态。

  • Modified(修改):该缓存行仅出现在此cpu缓存中,缓存已被修改,和内存中不一致,等待同步至内存。
  • Exclusive(独占):该缓存行仅出现在此cpu缓存中,缓存和内存中保持一致。
  • Shared(共享):该缓存行可能出现在多个cpu缓存中,且多个cpu缓存的缓存行和内存中的数据一致。
  • Invalid(失效):由于其他cpu修改了缓存行,导致本cpu中的缓存行失效。

# 如何规避伪共享

目前为止最有效的办法就是,缓存行填充。空间换时间。

Java8优雅解决:@sun.misc.Contended 注意:JVM启动参数加上-XX:-RestrictContended才会生效 最佳实践:ConcurrentHashMap.CounterCell

编辑 (opens new window)
#Queue
上次更新: 2025/01/19, 23:15:59
高性能无锁队列简介
漫谈JVM内存屏障

← 高性能无锁队列简介 漫谈JVM内存屏障→

最近更新
01
云原生资源
05-25
02
快速搭建Spring项目
03-27
03
kafka版本迭代说明
03-11
更多文章>
Theme by Vdoing | Copyright © 2018-2025 LiFengMing | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式