翻译自:http://mechanical-sympathy.blogspot.com/2011/07/false-sharing.html
伪共享
内存在缓存系统里是以缓存行为单位存储的。缓存行是大小为2的整数幂的连续字节,典型是32-256字节。最普遍的缓存行大小是64字节。伪共享是 个术语,用于当多个线程不知不觉地相互影响对方的性能,在修改使用相同缓存行的依赖变量时。缓存行上的写冲突是并行执行线程在SMP(对称多处理器 )系统上获得伸缩性的单一最大制约因素。我已听到伪共享被描述为无声的性能杀手,因为在查看代码时,它非常不明显。
为了达到按线程数量的线性伸缩性,我们必须确保没有两个线程写同一个变量或同一个缓存行。两个线程写同一个变量可以在代码层面跟踪到。为了知道独立 变量是否共享同一个缓存行,我们需要知道内存布局,或者让工具告诉我们。 Intel VTune是这样的一个分析工具。在这篇文章,我将解释Java对象的内存布局和如何通过填充缓存行来避免伪共享。
上图展示了伪共享的问题。线程A允许在Core 1,想更新变量X,运行在Core 2的线程想更新变量Y。不幸的是这两个热门变量属于同一个缓存行。每个线程将竞争缓存行的所有权,这样它们可以更新它。如果Core 1获得所有权,缓存子系统需要作废Core 2对应的缓存行。当Core 2获得所有权并执行它的更新,Core 1 将被告诉去作废它的缓存行拷贝。这将来来回回往返让L3缓存极大影响性能。如果竞争的Core处于不同的sockets,那么需要额外的跨Socket互 连,这个问题将更加恶化。
Java 内存布局
对于HotSpot JVM,所有对象都有一个2-字(world)的头。第一个是“标记(mark)”字,有24比特位用于哈希码和8比特位用于标记,例如锁状态,或交换用 于锁对象。第二个字是指向对象所属类的引用。数组有额外的字用于数组的大小。每个对象对齐到8字节的粒度边界,为了性能。因而为了更高效填充,对象的字段 将被重排序,从声明的顺序到下面的基于字节大小的顺序:
- doubles (8) and longs (8)
- ints (4) and floats (4)
- shorts (2) and chars (2)
- booleans (1) and bytes (1)
- references (4/8)
- <repeat for sub-class fields>
(译注:上面小括号里的数字表示占用的字节数。)
有了这些知识,我们可以用7个long在任意字段之间填充缓存行。在Disruptor里,我们在RingBuffer游标和BatchEventProcessor序号器周围填充缓存行。
为了展示性能影响,让我们用一些线程各自更新自己独立的计数器。这些计数器将是volatile
修饰的long
,这样将可以看到他们的进展。
public final class FalseSharing
implements Runnable
{
public final static int NUM_THREADS = 4; // change
public final static long ITERATIONS = 500L * 1000L * 1000L;
private final int arrayIndex;
private static VolatileLong[] longs = new VolatileLong[NUM_THREADS];
static
{
for (int i = 0; i < longs.length; i++)
{
longs[i] = new VolatileLong();
}
}
public FalseSharing(final int arrayIndex)
{
this.arrayIndex = arrayIndex;
}
public static void main(final String[] args) throws Exception
{
final long start = System.nanoTime();
runTest();
System.out.println("duration = " + (System.nanoTime() - start));
}
private static void runTest() throws InterruptedException
{
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < threads.length; i++)
{
threads[i] = new Thread(new FalseSharing(i));
}
for (Thread t : threads)
{
t.start();
}
for (Thread t : threads)
{
t.join();
}
}
public void run()
{
long i = ITERATIONS + 1;
while (0 != --i)
{
longs[arrayIndex].value = i;
}
}
public final static class VolatileLong
{
public volatile long value = 0L;
public long p1, p2, p3, p4, p5, p6; // comment out
}
}
结果
运行上面的代码,逐渐增加线程数量和添加/移除缓存行填充,我得到的结果如下图所示。这是衡量运行在我的4核 Nehalem 上的测试持续时间。
伪共享的影响是清晰可见的,通过完成测试需要的时间的增加。没有缓存行竞争,我们可以达到接近按线程线性的扩展。
这不是一个完美等测试,因为我们不能确定VolatileLongs将布局到内存的哪里。他们是独立的变量。然而,经验显示同时分配的对象倾向于同地协作(co-located)。
现在你知道,伪共享是无声的性能杀手了。
另一个测试例子
这是来自作者的另一篇文章:http://mechanical-sympathy.blogspot.com/2011/08/false-sharing-java-7.html,由于相关,就放一起了。
看起来Java 7变得更聪明了,会消除或重排序未使用的字段,这重新引入伪共享。作者认为下面的代码是最可靠的:
import java.util.concurrent.atomic.AtomicLong;
public final class FalseSharing
implements Runnable
{
public final static int NUM_THREADS = 4; // change
public final static long ITERATIONS = 500L * 1000L * 1000L;
private final int arrayIndex;
private static PaddedAtomicLong[] longs = new PaddedAtomicLong[NUM_THREADS];
static
{
for (int i = 0; i < longs.length; i++)
{
longs[i] = new PaddedAtomicLong();
}
}
public FalseSharing(final int arrayIndex)
{
this.arrayIndex = arrayIndex;
}
public static void main(final String[] args) throws Exception
{
final long start = System.nanoTime();
runTest();
System.out.println("duration = " + (System.nanoTime() - start));
}
private static void runTest() throws InterruptedException
{
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < threads.length; i++)
{
threads[i] = new Thread(new FalseSharing(i));
}
for (Thread t : threads)
{
t.start();
}
for (Thread t : threads)
{
t.join();
}
}
public void run()
{
long i = ITERATIONS + 1;
while (0 != --i)
{
longs[arrayIndex].set(i);
}
}
public static long sumPaddingToPreventOptimisation(final int index)
{
PaddedAtomicLong v = longs[index];
return v.p1 + v.p2 + v.p3 + v.p4 + v.p5 + v.p6;
}
public static class PaddedAtomicLong extends AtomicLong
{
public volatile long p1, p2, p3, p4, p5, p6 = 7L;
}
}
相关推荐
1.2 缓存行的概念1.3 伪共享(False Sharing)的概念 + 其可能引发的性能问题2 如何避免伪共享 — 数据填充2.1 不使用数据填充时的效率验证2.2 手动进行数据填充的效率验证2.3 通过java8新特性@sun.misc.Contended...
伪共享(False Sharing)是在多线程环境中出现的一种现象,特别是在涉及多核处理器的情况下更为显著。在现代计算机体系结构中,CPU缓存系统是以缓存行(Cache Line)为单位进行存储的。通常情况下,主流CPU的缓存行大小...
windows访问ubuntu很简单, 先在ubuntu上设置共享目录即可, 鼠标右键点目录,选择sharing options, 够选share this folder,需要的话也可以够选下面的allow write 这时在windows的网络邻居中的网络中查找就能找到...
【标题解析】:“八年级英语作文共享单车Bike-sharing”这篇标题表明了这是一篇针对初中生的英语写作,主题是关于共享单车(Bike-sharing)的现象及其影响,旨在锻炼学生的英语表达能力和对社会现象的理解。...
标题 "P2P_file_sharing_code.rar_Find Files_VB 文件共享_sharing files" 提供了我们讨论的关键点:这是一个基于VB(Visual Basic)编程语言实现的P2P(点对点)文件共享客户端,它的主要功能是让用户能够搜索并...
标题中的"DesktopSharing"指的是一个基于Windows桌面共享API开发的应用程序,它允许用户分享他们的桌面内容给其他人。在本文中,我们将深入探讨Windows桌面共享API的原理、使用C#编程语言实现桌面共享的方法,以及...
ChatGPT Plus 共享方案。ChatGPT Plus _ OpenAI API sharing solution.zip
《Easy File Sharing Web Server 7.2:便捷的文件共享解决方案》 Easy File Sharing Web Server 7.2 是一款高效且易用的文件共享软件,它专为希望通过网络轻松进行文件上传与下载的用户设计。这款系统使得在互联网...
1. **Sharing-aware Block Devices**:当数据进入内存时,系统会在页缓存中立即检查共享机会,从而实现实时的共享检测。 2. **Repayment FIFO**:如果共享失败,系统会将页面返回给Guest,确保资源的有效分配。 ...
并联电源的负载共享 load sharing,设计与介绍,多用在复杂通信、工业设备设计应用时,电源的备份,电源使用效率优化设计
虚假共享建造mvn clean packagedocker build -t false-sharing .跑docker run false-sharing
该项目为基于Vue框架构建的Campus-Cloud-Sharing校园师生资料共享系统,源码共计827个文件,涵盖了163个JavaScript文件、162个SVG图形文件、123个Java文件、79个GIF图像文件、53个CSS样式文件、47个Vue组件文件、47...
secret-sharing, 一个秘密分裂秘密秘密秘密秘密共享方案的系统 秘密共享 一种用于分分和共享密钥( 就像比特比特 private 密钥)的图书馆,使用iframe共享方案。安装>>> pip install secretsharing示例用
android手机usb虚拟网卡驱动—手机虚拟网卡——Remote_NDIS_based_Internet_Sharing_Device 本文件为XP系统电脑连接手机后将手机的上网资源分享给电脑使用。 tips:使用手机流量给电脑上网可以大量消耗你的手机流量...
VirtualHereUSB共享器.zipVirtualHereUSB共享器.zipVirtualHereUSB共享器.zipVirtualHereUSB共享器.zipVirtualHereUSB共享器.zipVirtualHereUSB共享器.zipVirtualHereUSB共享器.zipVirtualHereUSB共享器....
智能算法:Gaining-sharing knowledge based algorithm基于知识获取共享的算法
Easy File Sharing Web Server允许用户不需要任何附加的软件或服务就可以架设一个安全的、基于网页的P2P文件分享、传输系统。 除了HTML网页界面设计,Easy File Sharing Web Server还可以让你直接在自己的PC上快速...
### IBM PowerVM Virtualization Active Memory Sharing:深度解析与关键技术 #### 概览 IBM PowerVM Virtualization Active Memory Sharing(简称AMS)是IBM为提升Power Systems上虚拟化环境内存使用效率和灵活性...
借助 Delta Sharing,访问共享数据的用户可以通过 pandas、Tableau、Apache Spark、Rust 或其他支持开放协议的系统直接连接到共享数据,而无需先部署特定的计算平台。 数据提供者可以一次共享数据集以覆盖广泛的...