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 のサンプルプログラムで、コメントを追加しました. (ここで原作者に謝ります)
実行結果:
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のヒット回数を確認します.
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のヒット回数を確認します.