复习:java也需要考虑伪共享问题

先看两个概念

  1. 内存对齐:计算机中内存空间都是按照字节划分,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个排放

  2. 缓存行:主存中缓存是以缓存行(cache line)形式存储,其大小是2的整数幂,一般为32到256个字节,64最为常见。伪共享是指多个线程修改同一缓存行中的变量,就会造成性能下降(参考)。

所以内存访问都是一块块的,如果两个变量,存放在一个缓存行,那么core的每次访问或者修改,都会出现竞争;通过补全对象占用的空间,也就是内存对齐后,我们读取的都是一个整块,cpu处理速度会大大提升(提醒,性能提升的同时,可移植性有所降低)


看下代码(摘录,简单修改)测试,结果还是比较明显:

/**
 * 伪共享 测试
 */
public class CopyOfFalseSharing implements Runnable {

	public final static int NUM_THREADS = 500;
	public final static long ITERATIONS = 10000;
	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 CopyOfFalseSharing(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) / 1000);
	}

	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 CopyOfFalseSharing(i));
		}

		for (Thread t : threads) {
			t.start();
			//t.join();
		}
	}

	public void run() {
		long i = ITERATIONS + 1;
		while (0 != --i) {
			longs[arrayIndex].value = i;
			// System.out.println(arrayIndex+" = " +longs[arrayIndex].value);
		}
	}

	// 默认填充8个字节 =8个long字段  
	public final static class VolatileLong {
		public long p1, p2, p3, p4, p5, p6, p7; //缓冲行填充,放到value前面,4个线程耗时10秒
		public volatile long value = 0L;
		public long p9, p10, p11, p12, p13, p14, p15, p16; // 缓冲行填充,放到value后面,4个线程耗时26秒
	}

}

发布了25 篇原创文章 · 获赞 9 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/z390174504/article/details/53991972