False sharing with non-volatile state

307 Views Asked by At

Can false sharing occur with the following state:

Class Foo{

   int x;
   int y;

}

Whlie two threads are modifying concurrently x and y? Or is it not possible to judge as compiler might optimize x and y to registers?

3

There are 3 best solutions below

1
On

My test prove same thing as yours.

I'm also using com.lmax.disruptor.Sequence to test.

non-volatile long > volatile long use lasyset(Sequence.set) > Sequence.setVolatile == volatile long with padding > volatile long without padding

0
On

Of course it could happen (could being the keyword here), you can't tell for sure if these two variables will end-up on the same cache-line obviously. Compilers will not do anything (at least javac) to prevent such scenarios as making these variables to be held on different cache lines would be probably very expensive and it would require a lot of proof that is actually needed.

And yes also, your comment is correct, cache invalidation would happen quite often and might be a cause of a bottleneck. But measuring this is not easy, you can see an example here.

Just notice that since jdk-8 there is @Contended that would pad entries to fit on exactly a cache line.

1
On

test case like this:

public class FalseSharing implements Runnable {
public final static int NUM_THREADS = 2; // change

public final static long ITERATIONS = 50L * 1000L * 1000L;
private static VolatileLong[] longs = new VolatileLong[NUM_THREADS];

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

private final int arrayIndex;

public FalseSharing(final int arrayIndex) {
    this.arrayIndex = arrayIndex;
}

public static void main(final String[] args) throws Exception {
    final long start = System.currentTimeMillis();
    runTest();
    System.out.println("duration = " + (System.currentTimeMillis() - 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 long p1, p2, p3, p4, p5, p6, p7; // padding
    public long value = 0L; // change to volatile when test
    public long p1_1, p2_1, p3_1, p4_1, p5_1, p6_1, p7_1; // padding
}}

test result(5 times)(run in Intel Core i5 with 2 core):

  • volatile without padding(ms) 1360 1401 1684 1323 1383
  • volatile with padding(ms) 568 612 653 638 669
  • non-volatile without padding(ms) 41 35 40 35 44
  • non-volatile with padding(ms) 38 39 44 49 51

so in my opinion, false sharing will not occur with non-volatile field