伪共享(False Sharing)是高并发系统需要重点关注和优化的点。在计算机科学中,伪共享是多线程环境里的一种性能损失现象,当多个线程访问并修改处于同一缓存行内的不同数据时,会导致这个缓存行不断地在各个线程对应的缓存中切换,从而大大降低了系统性能,这就是所谓的伪共享现象。
在计算机中,缓存(Cache)用于协调速度差异较大的两种硬件之间数据传输速度的差异。例如,CPU 和主存之间就有 cache 进行协调,这种存在于硬件中的 Cache 常见的有 CPU Cache、GPU Cache 等;同时在软件中也有类似的存在,比如 Web Cache。
伪共享出现的主要原因在于硬件设计。每次缓存操作,CPU 并不是直接与主存交互,而是以缓存行为单位在主存和缓存间传递数据。如果多个线程同时修改一块内存区域的不同部分,并且这部分内存在同一缓存行内,那么即使这些修改本身无需同步,由于保存这些部分的缓存行会频繁地被标记为无效和重新读取,就会引起伪共享。
如何检测伪共享?
针对伪共享,硬件提供了特定的性能计数器来记录缓存行的命中次数和缓存行失效的次数。
另外,编程语言和特定的编程库也提供了相应的工具和方法来帮助我们检测和解决伪共享问题。例如,在 Java 中你可以使用 ThreadMXBean 接口,这是 Java 虚拟机的管理接口,可以用来监控虚拟机的线程和系统的状态。
如何解决伪共享问题?
解决伪共享的基本思路是尽量让各线程修改的内存不落在同一个缓存行上。常用的解决办法有 3 种。
- 数据对齐:通过数据对齐,可以将多线程操作的数据对齐到缓存行的边界,这样就不会出现一个缓存行上存在两个线程操作的数据的情况。这是最直接和最有效的解决办法。
- 数据隔离:在多线程操作的数据之间插入若干无用数据,使这些数据占用多个缓存行,从而避免伪共享。这种办法的缺点是会浪费大量内存。
- 使用无锁数据结构:一些设计良好的无锁数据结构,如队列、栈、大部分无锁哈希表等,从设计之初就已考虑到了避免伪共享的问题。使用这些数据结构可以在一定程度上避免伪共享。
![](https://img.haomeiwen.com/i5638694/9e7ce98c91a7794a.png)
缓存是计算机用来管理、协调不同速度硬件间数据传输差异的工具,这意味着它充当了 CPU 和主存之间的“中介”,来提高数据读取和写入的速度。缓存行则是 CPU 高速缓存中存储数据的单位。如果多个线程试图修改存在于同一个缓存行的不同数据,可能会导致所谓的伪共享现象,从而降低系统的整体性能。为了避免这一问题,我们可以采用数据对齐、数据隔离或使用无锁数据结构的方法。
特定的编程库和语言,如 Java 和 Disruptor,也提供了工具和方法来解决这一问题。在 Disruptor 中,每个数据项被一定长度的“填充”隔离开,确保每个数据项独立存在于不同的 Cache Line 中,从而提高了数据的并行访问效率。
此文章为10月Day17学习笔记,内容来源于极客时间《云时代的JVM》,强烈推荐该课程
网友评论