java硬件编程_关于JAVA并发编程你需要知道的——硬件篇
無論程序語言如何千變?nèi)f化,他們都深深地根植于目前的計算機(jī)體系結(jié)構(gòu)。
左圖是intel CPU的三級高速緩存設(shè)計,由于高速緩存對程序員基本不可見,因此可以抽象為右圖。
緩存的設(shè)計
首先還是先談?wù)勛髨D。
L1-cache分為兩部分,i-cache存儲指令(只讀),d-cache存儲數(shù)據(jù)(可讀可寫)
CPU只能和寄存器以及L1-cache進(jìn)行直接交互,數(shù)據(jù)不能隔層傳遞,只能一層一層往上讀,一層一層往下寫
訪問L1需要至少4個時鐘周期,L2需要至少10個,L3需要至少30個。即便是速度最快的L1,也低于運算單元的執(zhí)行速度,何況存在緩存未命中的情況,因此在L1和運算單元之間加上了Writebuffer和Readbuffer(合稱Memory Ordering Buffer,MOB),數(shù)據(jù)準(zhǔn)備好的時候再完成相關(guān)指令,這就是CPU指令亂序——順序執(zhí)行亂序完成。亂序完成的結(jié)果放入到Writebuffer中,按照原有的執(zhí)行順序,刷到緩存中
緩存由多個緩存行組成。每個緩存行結(jié)構(gòu)如圖所示(以64位機(jī)器為例)
CPU讀取緩存的時候找到對應(yīng)的緩存行,如果前面的有效位為零,就從下一級緩存加載到這一級緩存
相關(guān)的問題
明白緩存的設(shè)計之后,再看右圖來分析其中的問題
緩存導(dǎo)致的內(nèi)存可見性:已知線程A運行在core 0上,線程B運行在core 1上,兩者都對同一個內(nèi)存地址進(jìn)行讀取,這個內(nèi)存地址的內(nèi)容會被加載到cache,然后CPU讀取,這時候線程A對內(nèi)容進(jìn)行了修改,但是線程B卻可能一直從本核心的cache讀取,無法感知到該地址的內(nèi)容已被修改。
多核導(dǎo)致的自增操作原子性:自增操作分為三步:從內(nèi)存讀取變量到寄存器;寄存器中的值加1;寫回到內(nèi)存。已知線程A運行在core 0上,線程B運行在core 1上,兩者都對變量執(zhí)行加一操作。A執(zhí)行完一二兩步時,B執(zhí)行完第一步,A將加一后的值寫入到內(nèi)存,B執(zhí)行完二三兩步也將加一后的值寫入到內(nèi)存,結(jié)果變量只加了一,而不是加二
MOB導(dǎo)致的cache可見性:a=1.0; a=a/2; a=a-1.0;按照正常的邏輯,a最后的結(jié)果為-0.5;但是因為除法的執(zhí)行時鐘周期大于減法,第三句執(zhí)行時,a/2的結(jié)果存放在writebuffer中還沒寫入到緩存,a-1.0中a的值已經(jīng)從緩存中加載到readbuffer,也就是a-1.0=1.0-1.0=0 (高級語言不會出現(xiàn)這個問題,因為編譯器已經(jīng)做了處理,前面的偽代碼僅表示邏輯)
相關(guān)的實現(xiàn)
為了解決這些問題,CPU提供了一些指令,其中比如lock和cmpxchg。
lock 匯編前綴,在Intel奔騰系列之前,這個指令前綴能夠鎖定總線,禁止其他CPU核心操作內(nèi)存,執(zhí)行完后邊的指令后釋放總線,在這個過程中其他CPU核心會監(jiān)聽總線,發(fā)現(xiàn)某個內(nèi)存地址內(nèi)容被修改,就會將本核心下的對應(yīng)cache行有效位置0。因為鎖總線會禁止所有內(nèi)存操作,降低效率,因此在奔騰之后,這個指令前綴不鎖總線而是鎖定相關(guān)的cache行,對某個地址修改后直接讓相關(guān)cache失效。這樣解決了問題一
cmpxchg 將寄存器a中的值與內(nèi)存中比較如果一樣,將寄存器c中的值和內(nèi)存中的值交換,如果不一樣就設(shè)置異常位并將內(nèi)存中的值讀取到寄存器a。代入到問題二,從內(nèi)存讀取值到寄存器a,加一后保存到寄存器c,然后執(zhí)行cmpxchg,執(zhí)行完后如果有異常,就重新加一,再嘗試寫回,直到成功。這樣解決了問題二
cmpxchg和lock 在執(zhí)行完以后會將writebuffer刷到cache并清空readbuffer,這樣解決了問題三。另外X86_64引入了內(nèi)存屏障指令 lfence、sfence、mfence。lfence前面的讀取操作完成,也就是readbuffer中的內(nèi)容全部被cpu讀取后,才能執(zhí)行l(wèi)fence之后的讀取操作;sfence前面的寫入操作完成,也就是writebuffer全部刷到緩存中,才能執(zhí)行sfence之后的寫入操作;mfence之前的讀寫操作全部完成,才能進(jìn)行mfence之后的操作。
總結(jié)
以上是生活随笔為你收集整理的java硬件编程_关于JAVA并发编程你需要知道的——硬件篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 企业信息安全整体架构
- 下一篇: 中台建设方案