キャッシュライン
CPUメモリはCPUの速度を向上させるために、はるかに速い速度よりもあるので、CPUキャッシュは(キャッシュ)を添加し、キャッシュは、三L1、L2、L3に分割されます。小さい近いCPUのレベル、より速く、より小さな容量の割合。各キャッシュ内部キャッシュの動作の単位で保存されています。キャッシュラインは、典型的には2つの連続バイト、32から256バイトの整数乗であり、最も一般的なキャッシュラインサイズが64バイトです。
CPUがメモリをアクセスすると、最初のキャッシュキャッシュされたデータかどうかを確認します。ない場合は、キャッシュメモリからデータをロードする必要があり、最終的にはCPUに返す。そうであれば、データがアクセス・メモリなしで返されます。キャッシュヒット率場合は、パフォーマンスを大幅に向上させることができます。
キャッシュラインヒットテストコード
8の種類は8 * 1000000、シーケンシャルアクセス、ジャンプのアクセス時間の比較の長い配列を確立し、対照的なために、長い間、64バイトの8種類が長いキャッシュラインフィルできるバイト:
シーケンシャルアクセス:
public final class CacheLineTest {
//填充10000000个缓存行,每行8个long,共64字节
private static final long[] values = new long[8 * 10000000];
public static void main(String[] args) throws Exception {
long time = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
//顺序存取
values[i] = i;
}
time = System.nanoTime() - time;
System.out.println("顺续存取耗时:" + time);
}
}
結果は以下の通りであります:
ジャンプのアクセス:
public final class CacheLineTest {
//填充10000000个缓存行,每行8个long,共64字节
private static final long[] values = new long[8 * 10000000];
public static void main(String[] args) throws Exception {
long time = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
//跳跃存取
values[i*8] = i;
}
time = System.nanoTime() - time;
System.out.println("跳跃存取耗时:" + time);
}
}
結果は以下の通りであります:
千万次のアクセス時に、ギャップは非常に明白です。
偽の共有
そこに二つの変数間に関係はないが、それでも、スレッド間の同期を必要とするものの、二つの変数X、Yは、マルチスレッドアクセス、CPU1のスレッド上で実行されているアクセスX、CPU2アクセスY上で実行中のスレッドがあると仮定する。例えば、CPU1のスレッドがパフォーマンスに影響を与え、CPU2は、Y、共感、Y用CPU2更新もXに影響するアクセスできないとき繰り返し、キャッシュヒットで、その結果、失敗する時に更新されていない同じ行にX、Yを変更しました。
これを避けるために、一つの可能なアプローチは、キャッシュラインのキャッシュラインを埋めるためで、1つの変数のみが実際に効果的です。
偽共有テストコード
オブジェクトを宣言するDEMO、オブジェクトヘッダは、4つの例から、ほとんど同じキャッシュラインに、4つのスレッドのそれぞれを4つの連続したインスタンスを作成し、long型が8バイト、16バイトを占有し、8つのバイトを占め値は、その実行中の時刻を表示するために、アクセス操作の多くを行います。その後長い間、埋めるために64のバイトが、その実行中の時間を確認するために再実行するデモ6つの8バイトで満たされました。
public final class FlashShareDemo {
//测试对象,对象头占8字节,声明一个long类型8字节,
//共16字节,连续声明4个demo对象,多半在同一个缓存行中
static class Demo{
//需要操作的对象声明为volatile以便其他线程可以看到其变化
public volatile long value = 0L;
//填充long,每个8字节,填充6个,共64字节,刚好一个缓存行
//private long P1,P2,P3,P4,P5,P6;
}
//启动线程对demo中的value值做大量存取操作
static final class TestThread extends Thread{
private Demo demo;
public TestThread(final Demo demo){
this.demo = demo;
}
@Override
public void run(){
long start = System.currentTimeMillis();
for(int i = 0 ; i < 100000000; i++){
demo.value = i;
}
start = System.currentTimeMillis() - start;
System.out.println(Thread.currentThread().getName() + "运行耗时" + start);
}
}
public static void main(String[] args) throws Exception{
Demo[] Demo = new Demo[4];
//启动四个线程进行测试
for(int i = 0; i < 4; i++){
Demo[i] = new Demo();
}
TestThread[] testThread = new TestThread[Demo.length];
for (int i = 0; i < Demo.length ; i++ ){
testThread[i] = new TestThread(Demo[i]);
}
long start = System.currentTimeMillis();
for(Thread t : testThread){
t.start();
}
for(Thread t : testThread){
t.join();
}
start = System.currentTimeMillis() - start;
System.out.println("未填充对象访问耗时:" + start);
}
}
次のように充填されていない結果は以下のとおりです。
はるかに長い最初のスタートのスレッドよりも、最速のスタートアップ、ほぼ同じ時間を実行し、その後の3つのスレッドを実行している最初のスレッドを見ることができます。
以下のように(コメントアウトフィルコードのデモリリース)を充填した後の結果は以下のとおりです。
お互いを乱すことなく、4つのスレッドは、ほぼ同じ時間に実行している、場合塗りつぶされていない最初のスレッドを開始することと等価です。
添付ファイル:
-RestrictContended:Sun.misc.Contendedが偽の共有を避けるために、Java8で提供@、あなたはランタイムJVM起動パラメータ-XXに設定する必要があります