高并发下的魔术-缓存行填充

缓存系统中缓存通常是以缓存行为单位存储。常见缓存行通常为64字节。所以一个缓存行中通常会存放几个变量,如果这几个独立变量被多线程修改时,会因为共享同一个缓存行而影响彼此的性能。如果能将独立变量填充满整个缓存行,那么多线程之间就不再是共享关系,而是各自占用多个缓存行,进而提高性能。这也就是缓存填充能提高多线程的关键理论所在。那么,真实情况是否确实如此呢,我们可以做个有趣的实验。一组让多个线程修改一个long类型数组,另一组是修改填充后的VolatileLong对象数组。然后比对各自所需要的时间。

public class FalseSharing implements Runnable {
    public static final int THREAD_NUM = 4;
    public final static long ITERATIONS = 500 * 1000 * 1000L;
    private int arrayIdx;
    private static VolatileLong[] longs = new VolatileLong[THREAD_NUM];

    static {
        for (int i = 0; i < longs.length; i++) {
            longs[i] = new VolatileLong();
        }
    }

    public FalseSharing(int arrayIdx) {
        this.arrayIdx = arrayIdx;
    }

    private static void runTest() throws InterruptedException {
        Thread[] threads = new Thread[THREAD_NUM];
        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();
        }
    }

    @Override
    public void run() {
        long i = ITERATIONS + 1;
        while (0 != --i) {
            longs[arrayIdx].value = i;
//            longs[arrayIdx].value++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        long startTime = System.currentTimeMillis();
        runTest();
        long endTime = System.currentTimeMillis();
        System.out.println("duration = " + (endTime - startTime));

    }

    public final static class VolatileLong {

        public volatile long value = 0L;

        public long p1, p2, p3, p4, p5, p6; // comment out

    }


}

  机器参数为双核intel core i5。通过调节线程数,每次调节运行5次取平均数。统计出花费的时间。得出的数据如下:

 

 从图中可见确实缓存填充会带来性能上的提升,尤其是随着线程数的增长,性能提升的效果就越明显。

参考:

1、http://ifeve.com/falsesharing/ 

2、《Java 并发编程的艺术》

3、《Java 并发编程实战》

posted @ 2020-08-04 16:07  程序实验室  阅读(359)  评论(0)    收藏  举报