摘要:從視角理解系統(tǒng)結(jié)構(gòu)連載關(guān)注我的微博鏈接了解最新動態(tài)眾所周知是計(jì)算機(jī)的大腦它負(fù)責(zé)執(zhí)行程序的指令內(nèi)存負(fù)責(zé)存數(shù)據(jù)包括程序自身數(shù)據(jù)同樣大家都知道內(nèi)存比慢很多其實(shí)在年前的頻率和內(nèi)存總線的頻率在同一個級別訪問內(nèi)存只比訪問寄存器慢一點(diǎn)兒由于內(nèi)存的發(fā)展受到
從Java視角理解系統(tǒng)結(jié)構(gòu)連載, 關(guān)注我的微博(鏈接)了解最新動態(tài)
眾所周知, CPU是計(jì)算機(jī)的大腦, 它負(fù)責(zé)執(zhí)行程序的指令; 內(nèi)存負(fù)責(zé)存數(shù)據(jù), 包括程序自身數(shù)據(jù). 同樣大家都知道, 內(nèi)存比CPU慢很多. 其實(shí)在30年前, CPU的頻率和內(nèi)存總線的頻率在同一個級別, 訪問內(nèi)存只比訪問CPU寄存器慢一點(diǎn)兒. 由于內(nèi)存的發(fā)展受到技術(shù)及成本的限制, 現(xiàn)在獲取內(nèi)存中的一條數(shù)據(jù)大概需要200多個CPU周期(CPU cycles), 而CPU寄存器一般情況下1個CPU周期就夠了.
CPU緩存網(wǎng)頁瀏覽器為了加快速度,會在本機(jī)存緩存以前瀏覽過的數(shù)據(jù); 傳統(tǒng)數(shù)據(jù)庫或NoSQL數(shù)據(jù)庫為了加速查詢, 常在內(nèi)存設(shè)置一個緩存, 減少對磁盤(慢)的IO. 同樣內(nèi)存與CPU的速度相差太遠(yuǎn), 于是CPU設(shè)計(jì)者們就給CPU加上了緩存(CPU Cache). 如果你需要對同一批數(shù)據(jù)操作很多次, 那么把數(shù)據(jù)放至離CPU更近的緩存, 會給程序帶來很大的速度提升. 例如, 做一個循環(huán)計(jì)數(shù), 把計(jì)數(shù)變量放到緩存里,就不用每次循環(huán)都往內(nèi)存存取數(shù)據(jù)了. 下面是CPU Cache的簡單示意圖.
隨著多核的發(fā)展, CPU Cache分成了三個級別: L1, L2, L3. 級別越小越接近CPU, 所以速度也更快, 同時也代表著容量越小. L1是最接近CPU的, 它容量最小, 例如32K, 速度最快,每個核上都有一個L1 Cache(準(zhǔn)確地說每個核上有兩個L1 Cache, 一個存數(shù)據(jù) L1d Cache, 一個存指令 L1i Cache). L2 Cache 更大一些,例如256K, 速度要慢一些, 一般情況下每個核上都有一個獨(dú)立的L2 Cache; L3 Cache是三級緩存中最大的一級,例如12MB,同時也是最慢的一級, 在同一個CPU插槽之間的核共享一個L3 Cache.
| 從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|
| 內(nèi)存 | ~120-240 cycles | ~60-120ns|
感興趣的同學(xué)可以在Linux下面用cat /proc/cpuinfo, 或Ubuntu下lscpu看看自己機(jī)器的緩存情況, 更細(xì)的可以通過以下命令看看:
$ cat /sys/devices/system/cpu/cpu0/cache/index0/size 32K $ cat /sys/devices/system/cpu/cpu0/cache/index0/type Data $ cat /sys/devices/system/cpu/cpu0/cache/index0/level 1 $ cat /sys/devices/system/cpu/cpu3/cache/index3/level 3
就像數(shù)據(jù)庫cache一樣, 獲取數(shù)據(jù)時首先會在最快的cache中找數(shù)據(jù), 如果沒有命中(Cache miss) 則往下一級找, 直到三層Cache都找不到,那只要向內(nèi)存要數(shù)據(jù)了. 一次次地未命中,代表取數(shù)據(jù)消耗的時間越長.
緩存行(Cache line)為了高效地存取緩存, 不是簡單隨意地將單條數(shù)據(jù)寫入緩存的.?緩存是由緩存行組成的, 典型的一行是64字節(jié).
讀者可以通過下面的shell命令,查看cherency_line_size就知道知道機(jī)器的緩存行是多大.
$ cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size 64
CPU存取緩存都是按行為最小單位操作的. 在這兒我將不提及緩存的associativity問題, 將問題簡化一些. 一個Java long型占8字節(jié), 所以從一條緩存行上你可以獲取到8個long型變量. 所以如果你訪問一個long型數(shù)組, 當(dāng)有一個long被加載到cache中, 你將無消耗地加載了另外7個. 所以你可以非??斓乇闅v數(shù)組.
實(shí)驗(yàn)及分析我們在Java編程時, 如果不注意CPU Cache, 那么將導(dǎo)致程序效率低下. 例如以下程序, 有一個二維long型數(shù)組, 在我的32位筆記本上運(yùn)行時的內(nèi)存分布如圖:
32位機(jī)器中的java的數(shù)組對象頭共占16字節(jié)(詳情見?鏈接), 加上62個long型一行l(wèi)ong數(shù)據(jù)一共占512字節(jié). 所以這個二維數(shù)據(jù)是順序排列的.
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 { 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)); } }
編譯后運(yùn)行,結(jié)果如下
$ java L1CacheMiss starting.... duration = 1460583903
然后我們將22-26行的注釋取消, 將28-32行注釋,
編譯后再次運(yùn)行,結(jié)果是不是比我們預(yù)想得還糟?
$ java L1CacheMiss starting.... duration = 22332686898
前面只花了1.4秒的程序, 只做一行的對調(diào)要運(yùn)行22秒. 從上節(jié)我們可以知道在加載longs[i][j]時, longs[i][j+1]很可能也會被加載至cache中, 所以立即訪問longs[i][j+1]將會命中L1 Cache, 而如果你訪問longs[i+1][j]情況就不一樣了, 這時候很可能會產(chǎn)生 cache miss導(dǎo)致效率低下.
下面我們用perf來驗(yàn)證一下,先將快的程序跑一下.
$ perf stat -e L1-dcache-load-misses java L1CacheMiss starting.... duration = 1463011588 Performance counter stats for "java L1CacheMiss": 164,625,965 L1-dcache-load-misses 13.273572184 seconds time elapsed
一共164,625,965次L1 cache miss, 再看看慢的程序
$ perf stat -e L1-dcache-load-misses java L1CacheMiss starting.... duration = 21095062165 Performance counter stats for "java L1CacheMiss": 1,421,402,322 L1-dcache-load-misses 32.894789436 seconds time elapsed
這回產(chǎn)生了1,421,402,322次 L1-dcache-load-misses, 所以慢多了.
以上我只是示例了在L1 Cache滿了之后才會發(fā)生的cache miss. 其實(shí)cache miss的原因有下面三種:
第一次訪問數(shù)據(jù), 在cache中根本不存在這條數(shù)據(jù), 所以cache miss,可以通過prefetch解決.
cache沖突, 需要通過補(bǔ)齊來解決.
就是我示例的這種, cache滿, 一般情況下我們需要減少操作的數(shù)據(jù)大小, 盡量按數(shù)據(jù)的物理順序訪問數(shù)據(jù).
具體的信息可以參考這篇論文.
by MinZhou via ifeve
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/64052.html
摘要:從視角理解系統(tǒng)結(jié)構(gòu)連載關(guān)注我的微博鏈接了解最新動態(tài)從我的前一篇博文中我們知道了緩存及緩存行的概念同時用一個例子說明了編寫單線程代碼時應(yīng)該注意的問題下面我們討論更為復(fù)雜而且更符合現(xiàn)實(shí)情況的多核編程時將會碰到的問題這些問題更容易犯連包作者大師的 從Java視角理解系統(tǒng)結(jié)構(gòu)連載, 關(guān)注我的微博(鏈接)了解最新動態(tài) 從我的前一篇博文中, 我們知道了CPU緩存及緩存行的概念, 同時用一個例子說...
摘要:本文是從視角理解系統(tǒng)結(jié)構(gòu)連載文章在高性能編程時經(jīng)常接觸到多線程起初我們的理解是多個線程并行地執(zhí)行總比單個線程要快就像多個人一起干活總比一個人干要快然而實(shí)際情況是多線程之間需要競爭設(shè)備或者競爭鎖資源,導(dǎo)致往往執(zhí)行速度還不如單個線程在這里有一個 本文是從Java視角理解系統(tǒng)結(jié)構(gòu)連載文章 在高性能編程時,經(jīng)常接觸到多線程. 起初我們的理解是, 多個線程并行地執(zhí)行總比單個線程要快, 就像多個...
摘要:多線程技術(shù)是個很龐大的課題,編程思想這本書英文版,以下簡稱中也用了頁介紹的多線程體系。一個線程歸屬于唯一的進(jìn)程,線程無法脫離進(jìn)程而存在。五線程內(nèi)數(shù)據(jù)線程的私有數(shù)據(jù)僅歸屬于一個線程,不在線程之間共享,例如,,。 多線程技術(shù)是個很龐大的課題,《Java編程思想》這本書(英文版,以下簡稱TIJ)中也用了136頁介紹Java的多線程體系。的確,Java語言發(fā)展到今天,多線程機(jī)制相比其他的語言從...
摘要:經(jīng)過半年的沉淀,加上對,和分布式這塊的補(bǔ)齊,終于開始重拾面試信心,再次出征。面試官提示沒有提到線程的有內(nèi)核態(tài)的切換,程只在用戶態(tài)調(diào)度。三面綜合技術(shù)面這面面的是陣腳大亂,面試官采用刨根問底的方式提問,終究是面試經(jīng)驗(yàn)不夠,導(dǎo)致面試的節(jié)奏有點(diǎn)亂。 經(jīng)過半年的沉淀,加上對MySQL,redis和分布式這塊的補(bǔ)齊,終于開始重拾面試信心,再次出征。 鵝廠 面試職位:go后端開發(fā)工程師,接受從Jav...
閱讀 1438·2021-09-28 09:44
閱讀 2501·2021-09-28 09:36
閱讀 1144·2021-09-08 09:35
閱讀 1982·2019-08-29 13:50
閱讀 810·2019-08-29 13:29
閱讀 1130·2019-08-29 13:15
閱讀 1724·2019-08-29 13:00
閱讀 2988·2019-08-26 16:16