cpuキャッシュjavaパフォーマンスの問題の初期プローブ

3830 ワード

メモリとcpuレジスタの間には、cpuキャッシュ、すなわちcacheと呼ばれる領域もあります.
CacheはL 1、L 2、L 3の3つのレベルのキャッシュに分けられ、速度は減少し、cpuからますます遠くなり、L 1、L 2の各コア自体があり、L 3は各スロットの複数のコアが共通して1つである.cpuは値に応じてチャンネルを用いて1,2,3キャッシュから1つずつ検索し,L 1がヒットしなければ下へL 2,L 3をメモリまで検索し続ける.
CPUから
約必要なCPUサイクル
約所要時間(単位ns)
レジスタ
1 cycle
 
L1 Cache
~3-4 cycles
~0.5-1 ns
L2 Cache
~10-20 cycles
~3-7 ns
L3 Cache
~40-45 cycles
~15 ns
スロットトランスミッション
 
~20 ns
メモリ
~120-240 cycles
~60-120ns
上記キャッシュのアクセスと検索読み出しの操作では、最も基本的な単位(すなわちカーネル操作キャッシュの最も基本的な単位)がキャッシュライン(cache line)となり、一般的にcache lineのサイズは64バイトである.
例を挙げると、シーケンスに格納された1つの配列について、メモリから1つの配列要素を読み出してキャッシュに入れる場合、64バイトを一度に読み出すと、読み出す必要がある要素の後ろの8バイトlongもキャッシュに入れられ、1つの操作で完了し、追加の費用がかかりません.パフォーマンスが大幅に向上しました.
しかし、もし私たちのプログラムがわざと邪魔をして、cpuのこのような最適化メカニズムを妨害したら?
http://coderplay.iteye.com/blog/1485760  のサンプルプログラムで、コメントを追加しました. (ここで原作者に謝ります)
public class L1CacheMiss {  
    private static final int RUNS = 10;  
    private static final int DIMENSION_1 = 1024 * 1024;  
    private static final int DIMENSION_2 = 62;
  
    private static long[][] longs;  
  
    public static void main(String[] args) throws Exception {
            
            /*     1024*1024 、62      (  ),         0*/  
        Thread.sleep(10000);  
        longs = new long[DIMENSION_1][];  
        for (int i = 0; i < DIMENSION_1; i++) {  
            longs[i] = new long[DIMENSION_2];  
            for (int j = 0; j < DIMENSION_2; j++) {  
                longs[i][j] = 0L;  
            }  
        }  
        System.out.println("starting....");  
  
        final long start = System.nanoTime();  
        long sum = 0L;  
        for (int r = 0; r < RUNS; r++) {  
            /**/
//          for (int j = 0; j < DIMENSION_2; j++) {  
//              for (int i = 0; i < DIMENSION_1; i++) {  
//                  sum += longs[i][j];  
//              }  
//          }  
                  /**/
            for (int i = 0; i < DIMENSION_1; i++) {  
                for (int j = 0; j < DIMENSION_2; j++) {  
                    sum += longs[i][j];  
                }  
            }  
        }  
        System.out.println("duration = " + (System.nanoTime() - start));  
    }  
}  

実行結果:
starting....       duration = 679,818,029          sum = 0
starting....       duration = 12,850,546,522     sum = 0
まず行を1秒も遍歴しないで、先に列を遍歴すると12秒かかります! 
linux環境でperfを使用できる場合 stat -e L1-dcache-load-misses java L1CacheMiss   コマンドを使用して、L 1 cacheのヒット回数を確認します.