Scenario
Multiple threads rewriting consecutive different resources under multi-core CPU.
Mechanism
Because CPUs often use cache to speed up variable reading, the MESI protocol will cause the corresponding cache lines on different CPUs to become invalid when threads on different CPUs modify the corresponding cache. When another CPU modifies the variable in the cache line (even if it is not the same variable), it will read from memory, which will cause the corresponding cache line of the original CPU to become invalid. This process repeats, causing the CPU to read and modify variables through memory, unable to effectively utilize the cache, thus slowing down the speed.
Code Implementation
- False Sharing
public final class FalseSharing
implements Runnable
{
public final static int NUM_THREADS = 2; // change
public final static long ITERATIONS = 500 * 1000 * 1000;
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 FalseSharing(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));
}
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 volatile long value = 0;
}
}
- No False Sharing
public final class FalseSharing
implements Runnable
{
public final static int NUM_THREADS = 2; // change
public final static long ITERATIONS = 500 * 1000 * 1000;
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 FalseSharing(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));
}
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;
}
}
@sun.misc.Contended
public final static class VolatileLong
{
public volatile long value = 0;
}
}
The JVM parameter -XX:-RestrictContended needs to be added.
Experimental Results
- False Sharing
- No False Sharing