zzh

zzh

伪填充問題

シナリオ

複数のスレッドが連続した異なるリソースを書き換える場合、マルチコア CPU 上で発生します。

発生メカニズム

CPU は通常、変数の読み取りを高速化するためにキャッシュを使用しますが、MESI プロトコルにより、異なる CPU 上のスレッドが対応するキャッシュを変更すると、他の CPU 上の対応するキャッシュ行が無効になります(存在する場合)。そのため、別の CPU がそのキャッシュ行の変数を変更すると(同じ変数でなくても)、メモリから読み取りが行われ、元の CPU の対応するキャッシュ行が無効になります。これが繰り返されるため、変数の変更読み取りはすべてメモリを経由して行われ、キャッシュを効果的に利用できなくなり、速度が低下します。

コードの実装
  • フォールスシェアリングが発生する場合
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;
    }
}
  • フォールスシェアリングが発生しない場合
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;
    }
}

ここには、jvm パラメータ - XX:-RestrictContended を追加する必要があります。

実験結果
  • フォールスシェアリングが発生する場合

image

  • フォールスシェアリングが発生しない場合

image

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。