伪共享(False Sharing)是一种性能问题,发生在多核处理器系统中,当多个线程对不同的变量进行操作时,如果这些变量位于同一缓存行(cache line)中,会导致不必要的缓存一致性流量,从而影响性能。即使这些线程操作的是不同的变量,但由于它们共享同一个缓存行,任何一个线程对其中一个变量的修改都会导致其他线程的缓存行失效,进而增加了缓存一致性开销。
示例代码
以下是一个Java示例,演示了伪共享的影响:
public class FalseSharingExample {
public static final int NUM_THREADS = 4;
public static final long ITERATIONS = 50L * 1000L * 1000L;
private static final VolatileLong[] longs;
static {
longs = new VolatileLong[NUM_THREADS];
for (int i = 0; i < longs.length; i++) {
longs[i] = new VolatileLong();
}
}
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Writer(i));
}
long start = System.currentTimeMillis();
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
long duration = System.currentTimeMillis() - start;
System.out.println("Duration with False Sharing: " + duration + " ms");
}
public static class Writer implements Runnable {
private final int threadId;
public Writer(int threadId) {
this.threadId = threadId;
}
@Override
public void run() {
long i = ITERATIONS + 1;
while (0 != --i) {
longs[threadId].value = i;
}
}
}
public static class VolatileLong {
public volatile long value = 0L;
// 伪共享的表现:不同线程的变量可能在同一个缓存行上
}
}
消除伪共享
为了消除伪共享,可以通过填充(padding)或对齐(alignment)技术,确保每个线程的变量位于不同的缓存行中。以下是修改后的示例,展示了如何使用填充字段来消除伪共享:
public class FalseSharingFixed {
public static final int NUM_THREADS = 4;
public static final long ITERATIONS = 50L * 1000L * 1000L;
private static final PaddedVolatileLong[] longs;
static {
longs = new PaddedVolatileLong[NUM_THREADS];
for (int i = 0; i < longs.length; i++) {
longs[i] = new PaddedVolatileLong();
}
}
public static void main(String[] args) throws Exception {
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Writer(i));
}
long start = System.currentTimeMillis();
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
long duration = System.currentTimeMillis() - start;
System.out.println("Duration with Padding: " + duration + " ms");
}
public static class Writer implements Runnable {
private final int threadId;
public Writer(int threadId) {
this.threadId = threadId;
}
@Override
public void run() {
long i = ITERATIONS + 1;
while (0 != --i) {
longs[threadId].value = i;
}
}
}
public static class PaddedVolatileLong {
public volatile long value = 0L;
// 填充字段,确保每个变量位于不同的缓存行中
public long p1, p2, p3, p4, p5, p6, p7;
}
}
总结
伪共享:当多个线程操作位于同一个缓存行中的不同变量时,可能导致缓存一致性流量增加,影响性能。
消除伪共享:
填充:通过在变量之间添加额外的字段来确保它们不位于同一个缓存行中。
对齐:确保每个变量占用独立的缓存行。
通过上述优化,可以减少伪共享对程序性能的影响,提升并发程序的效率。在实际应用中,监控和优化伪共享是一项重要的性能调优工作。