性能主题

Java性能优化要点之三: False-sharing伪共享

首页

  数据会在CPU的一个cache-line中发生竞争,每个cache-line大小是64字节,那么就用将一个对象填充满一个cache-line ,最好的办法是加入longs,当然在Java 8中@Contended,这样可以不允许多个对象拥挤在一个cache-line中.

下面看看如何填满一个Cache-line,我们进行填满与无填满一个Cache-line的测试,测试代码如下:

public final class FalseSharing implements Runnable {

  private static AtomicLong[] longs = new AtomicLong[NUM_THREADS];

  ....//此处有无填充和有填充区别,见后面

  public 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 final class FalseSharing implements Runnable {

  private static AtomicLong[] longs = new AtomicLong[NUM_THREADS];

  static {

    for (int i = 0; i < longs.length; i++) {

      longs[i] = new AtomicLong();

    }

  }

  ...

}

下面是填满一个Cacheline的AtomicLong代码:

public final class FalseSharing implements Runnable {

  private static PaddedAtomicLong[] longs = new PaddedAtomicLong[NUM_THREADS];

  static {

    for (int i = 0; i < longs.length; i++) {

      longs[i] = new PaddedAtomicLong();

    }

  }

  ...

  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;

  }

}

测试性能结果:

图中红色图块表示没有填满Cache-line发生了伪共享,而蓝色色块是填满了,随着线程数的增加,伪共享增加了执行时间。性能差。

下页

Disruptor