偽共有のプロフィール

フォルス・シェアリングとは何ですか

CPUキャッシュ

我々はすべてのCPUの処理速度とハードドライブを知っているように、メモリアクセス速度の差が大きすぎると、あなたはそれ以外の場合は、全体的なCPUのスループットが大きく影響を受けることにつながる、CPUのキャッシュによって実行されている必要があります。

次のように関係なく、価格の単層キャッシュヒット率、今CPUレベル3キャッシュ構造の多くがあった、要件を満たすためにスピードを見つけることができません、アクセス速度は、以下のとおりです。

CPUキャッシュ遅延ユニットは、CPUクロックサイクルは、CPUの命令実行時間として理解することができます

L1はL2、L3のサブセットであり、L2は、キャッシュ容量が徐々に増加L1 L3に、順次、時間のかかるCPUの増加する順序を見つけるためには、L1、L2、L3、メインメモリを見つけることサブセットです。CPUコアに対応したL1、単核排他的、他の核問題なく改変。L2は、排他一般単核です。マルチコアは、一般L3を共有し、その後、問題がある可能性があり、同じデータを操作することができます。

キャッシュライン

現代のCPUは、データは、典型的には、連続したブロック単位、すなわち、キャッシュライン(キャッシュライン)で読み取ります。メモリアレイは、一般に連続的に割り当てられているので、したがって記憶されたデータへの連続アクセスが高速ランダムアクセスよりも、通常、アクセスは、アレイ構造の鎖状構造よりも一般的に高速です。

PS。JVM標準が提供しない「配列は連続した領域に配置されなければならない、」いくつかのJVM実装は、大きな配列が連続空間にない割り当てます。

キャッシュラインサイズが偶数のみ1バイトのデータを操作すると、CPUは、このデータが配置されているデータの64連続したバイトの最小値を読み取ることを意味し、典型的には64バイトです。

キャッシュの無効化

キャッシュの有効性を確保するために単にMESIプロトコル、他の核主流のCPUに応じて変更し、キャッシュ・ラインが失敗する、ために再読み込みキャッシュを必要とする場合、核が使用されている場合、キャッシュ・ライン・データを理解しました。

偽の共有

コア内の複数のスレッドが異なる可変データ・キャッシュ・ラインで動作する場合、頻繁にキャッシュの無効化が存在するであろう、二つのスレッド間には何の関係もコードレベルのデータで動作していない参照。

この非合理的な競争疑似科学的な名前のリソース共有(偽共有が)、真剣に同時機械の効率に影響を与えます。

擬似シェア例

// 多个线程,每个线程操作一个VolatileLong数组中的元素
// VolatileLong是否进行填充会影响最终结果
// 为填充时会产生伪共享问题,运行更慢,填充后不会
public class FalseShareTest implements Runnable {
    public static int NUM_THREADS = 4;
    public final static long ITERATIONS = 50L * 1000L * 1000L;
    private final int arrayIndex;
    private static VolatileLong[] longs;
    public static long SUM_TIME = 0l;
    public FalseShareTest(final int arrayIndex) {
        this.arrayIndex = arrayIndex;
    }
    public static void main(final String[] args) throws Exception {
        Thread.sleep(10000);
		// 多个线程操作多个VolatileLong
        for(int j=0; j<10; j++){
		// 初始化
            System.out.println(j);
            if (args.length == 1) {
                NUM_THREADS = Integer.parseInt(args[0]);
            }
            longs = new VolatileLong[NUM_THREADS];
            for (int i = 0; i < longs.length; i++) {
                longs[i] = new VolatileLong();
            }
            final long start = System.nanoTime();
			// 构造并启动线程
            runTest();
            final long end = System.nanoTime();
            SUM_TIME += end - start;
        }
        System.out.println("平均耗时:"+SUM_TIME/10);
    }
    private static void runTest() throws InterruptedException {
		// 创建每个线程, 每个线程操作一个VolatileLong
        Thread[] threads = new Thread[NUM_THREADS];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new FalseShareTest(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 = 0L;
        public long p1, p2, p3, p4, p5, p6;     // 注释此行,结果区别很大
    }
}
复制代码

VolatileLong 6つの長い変数の充填を使用するかどうか、結果は多くを変えます。埋めるために使用し、より速く、フォルス・シェアリングを回避することができます。

フォルス・シェアリングを回避する方法

以下のJava8バージョン

以下のJava8バージョンなど、回避する方法を埋めるために使用することができるでは百度のスノーフレーク実現 PaddedAtomicLongを使用:

/**
 * Represents a padded {@link AtomicLong} to prevent the FalseSharing problem<p>
 * 
 * The CPU cache line commonly be 64 bytes, here is a sample of cache line after padding:<br>
 * 64 bytes = 8 bytes (object reference) + 6 * 8 bytes (padded long) + 8 bytes (a long value)
 * 
 * @author yutianbao
 */
public class PaddedAtomicLong extends AtomicLong {
    private static final long serialVersionUID = -3415778863941386253L;

    /** Padded 6 long (48 bytes) */
    public volatile long p1, p2, p3, p4, p5, p6 = 7L;

    /**
     * Constructors from {@link AtomicLong}
     */
    public PaddedAtomicLong() {
        super();
    }

    public PaddedAtomicLong(long initialValue) {
        super(initialValue);
    }

    /**
     * To prevent GC optimizations for cleaning unused padded references
     */
    public long sumPaddingToPreventOptimization() {
        return p1 + p2 + p3 + p4 + p5 + p6;
    }

}
复制代码

6長い変数48が充填されているバイト、および長い型の値、64バイトの合計を使用して、8バイトのオブジェクト参照。回避するために使用SumPaddingToPreventOptimization変数またはGCコンパイラの最適化が使用されていません。

Java8と上記のバージョン

フォルス・シェアリングを避けるために始めJava8ネイティブサポートからは、あなたが使用することができ@Contended、注釈を。

public class Point { 
    int x;
    @Contended
    int y; 
} 
复制代码

参照してください@Contended使用するノートを。

@Contendedノートには注意して使用して、ターゲット・インスタンスのサイズを大きくします。デフォルトでは、JDKクラスの内部に加えて、JVMは、コメントを無視します。(唯一のJDKクラス内部使用を意味する)をtrueに-RestrictContended =偽、それはデフォルト:-XXを設定し、それをサポートするためのコードを適用します。もちろん、-XXもある:EnableContented設定パラメータは、アノテーションフィーチャの開閉を制御するために、デフォルトではfalseにあれば、trueで、サイズConcurrentHashMapのスレッドクラスを減らすことができます。ページの "JavaパフォーマンスDefinitive Guideの" 210を参加。

参考資料

偽共有(フォルス・シェアリング)、並行プログラミングサイレントパフォーマンスキラー - ブログパーク

偽共有(偽の共有)、キャッシュライン(キャッシュライン)ごった煮 - ジェーンの本

移動この記事で私のブログでは、訪問する歓迎します!

おすすめ

転載: juejin.im/post/5d3804ac51882562146f0870