成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

深入理解并發編程藝術之計算機內存模型

開發 前端
隨著計算機高速發展,CPU 技術遠超過內存技術,所以多級緩存被使用,解決了內存和 cpu 的讀寫速度問題,隨著多線程的發展,緩存一致性問題油然而生,好在可以通過緩存一致性協議來解決,比較出名的緩存一致性協議是MESI,MESI協議的引入,微微降低了 cpu 的速度。

了解java內存模型不得不先了解計算機內存模型,我們接下來就從計算內存模型說起

計算機發展

我們都知道 CPU 和 內存是計算機中比較核心的兩個東西,任何在計算機上運行的程序其實都是對數據的存取和處理計算,最終都會映射成cpu和內存之間的頻繁交互,最原始計算機就是cpu讀取內存進行處理,然后回寫內存。

CPU在摩爾定律的指導下以每18個月翻一番的速度在發展,cpu的處理速度不斷增速,其處理速度遠遠超出了內存的讀寫速度,導致的后果就是cpu大量的時間都花費在磁盤 I/O、網絡通信或者數據庫訪問上,cpu大部分的時間都處于空閑的等待狀態。

為了充分壓榨cpu的性能,避免cpu性能浪費,就必須使用一些手段去把處理器的運算能力“壓榨”出來,最容易想到的就是讓計算機同時處理幾項任務。為了實現這一目標,計算機系統不得不加入一層或多層讀寫速度盡可能接近處理器運算速度的高速緩存(Cache)來作為內存與處理器之間的緩沖:將運算需要使用的數據復制到緩存中,讓運算能快速進行,當運算結束后再從緩存同步回內存之中,這樣處理器就無須等待緩慢的內存讀寫了。

圖片圖片

上圖為計算機多核cpu多級緩存圖,即當下流行的cpu架構,計算機內存模型主要涉及到的組件:處理器,寄存器,高速緩存,內存,緩存行。

處理器:負責做邏輯運算,程序代碼都會變成運算指令或計算公式,在處理器里面其實就是二進制的各種組合,處理器計算后會得到一個結果。

寄存器:離處理器最近的一塊存儲介質,可以說位于內存模型的頂端,它的速度非常之快,快到可以和處理器相媲美,處理器從里面拿數據,運算完之后又把數據存回去。寄存器是處理器里面的一部分,處理器可能有多個寄存器,比如數據計數器,指令指針寄存器等等。

高速緩存:是一個比內存速度快很多接近處理器速度的存儲區域,目的是把處理器要用到的一堆數據從主內存中復制進來供處理器使用,處理器運算處理完了之后又把結果同步回主內存,這樣處理器只做自己的事,而高速緩存就成了傳話筒。高速緩存有分為一級緩存,二級緩存和三級緩存,離處理器最近的是一級緩存,依次往后排。存儲器存儲空間大?。簝却?gt;L3>L2>L1>寄存器存儲器速度快慢排序:寄存器>L1>L2>L3>內存

緩存行:緩存是由最小的存儲區塊-緩存行(cacheline)組成,緩存行大小通常為64byte。緩存行是什么意思呢?比如你的L1緩存大小是512kb,而cacheline = 64byte,那么就是L1里有512 * 1024/64個cacheline,也是cpu中寄存器從緩存中取數據的最小單位,即取數為x=0,那么在緩存中找到x=0后不是只把x=0取走,而是把x=0所在的緩存行取走。

內存:就是我們通常講的內存,比如現在的電腦動不動8G,16G啊等等,在內存模型中叫做主內存,它比磁盤的讀寫速度快很多,但是又跟高速緩存沒法比,因此,程序啟動的時候,程序相關的數據會加載到主內存,然后處理器處理某塊邏輯的時候,比較占空間的東西會丟到主內存,比如Java里面的對象,就是存放在堆上面的,而Java虛擬機里面的堆就是放在主內存的。

在CPU訪問存儲設備時會遵循一定的原理,無論是存取數據抑或存取指令,都趨于聚集在一片連續的區域中,這就是局部性原理。這也是cpu架構提高性能的一個關鍵性因素。

時間局部性(Temporal Locality):如果一個信息項正在被訪問,那么在近期它很可能還會被再次訪問。比如循環、遞歸、方法的反復調用等。

空間局部性(Spatial Locality):如果一個存儲器的位置被引用,那么將來他附近的位置也會被引用。比如順序執行的代碼、連續創建的兩個對象、數組等。

帶有高速緩存的CPU執行計算的流程:1.程序以及數據被加載到主內存2.指令和數據被加載到CPU的高速緩存3.CPU執行指令,把結果寫到高速緩存4.高速緩存中的數據寫回主內存

講到這里我們知道以上新型cpu架構是為充分壓榨cpu性能而來,那么就單看以上架構,在不做任何優化的情況下,當多核cpu并發工作的時候必然會引入緩存一致性問題。在多路處理器系統中,每個處理器都有自己的高速緩存,而它們又共享同一主內存,當多個處理器的運算任務都涉及同一塊主內存區域時,將可能導致各自的緩存數據不一致。如果真的發生這種情況,那同步回到主內存時該以誰的緩存數據為準呢?例如:假設主內存中存在一個共享變量 x,現在有 A 和 B 兩個內核(也可以直接說分布在兩個核上的線程)分別對該變量 x=1 進行操作,A/B 核各自高速緩存中存在共享變量副本 x。假設現在 A 想要修改 x 的值為 2,而 B 卻想要讀取 x 的值,那么 B 讀取到的值是 A 更新后的值 2,還是更新前的值 1 呢?答案是,不確定,即 B 有可能讀取到 A 更新前的值 1,也有可能讀取到 A 更新后的值 2,這是因為高速緩存是每個核私有的數據區域,而 A 在操作變量 x 時,首先是將變量從主內存拷貝到 A 的高速緩存中,然后對變量進行操作,操作完成后再將變量 x 寫回主內存,而對于 B 也是類似的,這樣就有可能造成主內存與工作內存間數據存在一致性問題,假如 A 修改完后正在將數據寫回主內存,而 B 此時正在讀取主內存,即將 x=1 拷貝到自己的工作高速緩存中,這樣 B 讀取到的值就是 x=1,但如果 A 已將 x=2 寫回主內存后,B 才開始讀取的話,那么此時 B 讀取到的就是 x=2,但到底是哪種情況先發生呢,在并發訪問過程中這些都是不確定的。

除了增加高速緩存之外,為了使處理器內部的運算單元能盡量被充分利用,處理器可能會對輸入代碼進行亂序執行優化,處理器會在計算之后將亂序執行的結果重組,保證該結果與順序執行的結果是一致的,但并不保證程序中各個語句計算的先后順序與輸入代碼中的順序一致,因此如果存在一個計算任務依賴另外一個計算任務的中間結果,那么其順序性并不能靠代碼的先后順序來保證,顧名思義,當單線程運行的時候,無論怎樣亂序,最終的結果都是預期的結果,但是當多線程的時候呢,就不一定了,特別是存在共享變量的或者說一個線程依賴于另一個線程的計算結果的時候,就很有可能因為亂序帶來不正確的結果。

通過以上可以得知,cpu架構自身存在數據一致性的問題和亂序重排問題,其實也可以理解為java的并發訪問的原子性問題,可見性問題,有序性問題。

計算機內存模型

在多核cpu架構中,每個核心都有自己的L1 L2高速緩存,同個cpu的多個核心共享L3緩存,不同cpu之間共享主內存,為了保證共享內存的正確性,內存模型定義了共享內存系統中多線程程序讀寫操作行為的規范。通過這些規則來規范對內存的讀寫操作,從而保證指令執行的正確性。它與處理器有關、與緩存有關、與并發有關、與編譯器也有關。他解決了 CPU 多級緩存、處理器優化、指令重排等導致的內存訪問問題,保證了并發場景下的一致性、原子性和有序性。

內存模型解決并發問題主要采用兩種方式:1.限制處理器優化2.使用內存屏障

我們來看下內存模型的具體做法

解決緩存不一致問題

解決緩存不一致的方法有很多,比如:總線加鎖(此方法性能較低,現在已經不會再使用)MESI 協議:當一個 CPU 修改了 Cache 中的數據,會通知其他緩存了這個數據的 CPU,其他 CPU 會把 Cache 中這份數據的 Cache Line 置為無效,要讀取數據的話,直接去內存中獲取,不會再從 Cache 中獲取了。當然還有其他的解決方案,MESI 協議是其中比較出名的。

MESI 協議中的狀態CPU 中每個緩存行使用的 4 種狀態進行標記(使用額外的兩位 bit 表示)

圖片圖片

  • M 和 E 的數據都是本 core 獨有的,不同之處是 M 狀態的數據是 dirty(和內存中的不一致),E 狀態的數據是 clean(和內存中的一致)
  • S 狀態是所有 Core 的數據都是共享的,只有 clean 的數據才能被多個 core 共享
  • I-表示這個 Cache line 無效

E 狀態只有 Core 0 訪問變量 x,它的 Cache line 狀態為 E(Exclusive)。

圖片圖片

S 狀態3 個 Core 都訪問變量 x,它們對應的 Cache line 為 S(Shared)狀態。

圖片圖片

M 狀態和I狀態之間的轉化Core 0 修改了 x 的值之后,這個 Cache line 變成了 M(Modified)狀態,其他 Core 對應的 Cache line 變成了 I(Invalid)狀態 在 MESI 協議中,每個 Cache 的 Cache 控制器不僅知道自己的讀寫操作,而且也監聽(snoop)其它 Cache 的讀寫操作。每個 Cache line 所處的狀態根據本核和其它核的讀寫操作在 4 個狀態間進行遷移

圖片圖片

MESI 協議通過標識緩存數據的狀態,來決定 CPU 何時把緩存的數據寫入到內存,何時從緩存讀取數據,何時從內存讀取數據。

MESI 協議看似解決了緩存的一致性問題,但是并不那么完美,因為當多個緩存對數據進行了緩存時,一個緩存對數據進行修改需要同過指令的形式與其他 CPU 進行通訊,這個過程是同步的,必須其他 CPU 都把緩存里的數據都置為 Invalid 狀態成功后,我們修改數據的 CPU 才能進行下一步指令,整個過程中需要同步的和多個緩存通訊,這個過程是不穩定的,容易產生問題,而且通訊的過程中 CPU 是必須處于等待的狀態,那么也影響著 CPU 的性能。

為了避免這種 CPU 運算能力的浪費,解決 CPU 切換狀態阻塞,Store Bufferes 被引入使用。處理器把它想要寫入到主存的值寫到緩存,然后繼續去處理其他事情。當所有失效確認都接收到時,數據才會最終被提交。

指令重排問題

public class config{
    // 此變量必須定義為
 1   boolean initialized = false;
 2   public Object cache(@NotNull String key) {
 3       if (!initialized) {
 4           doSomethingWithConfig();
 5       }
 6       configText = readConfigFile("pz");
 7       processConfigOptions(configText, "xx");
 8       initialized = true;
 9       if (!initialized) {
 10           doSomethingWithConfig();
        }
    }  
}

拿上面的代碼來說明下亂序,簡單來講就是initialized = false;cpu為了高效,避免再次去緩存取值,很有可能接著執行initialized = true(判斷為無依賴關系的情況下),這個時候6、7行還沒有執行,單線程情況下不會有問題,但是并發情況下就會有問題。下一篇我們詳細講解。

指令重排序解決方案:硬件工程師其無法預知未知的程序邏輯場景,所以一些問題還是遺留給了軟件工程師,但是他們給我們提供了一套對應場景的解決方案就是“內存屏障指令”,我們的軟件工程師可以同內存屏障來針對不同場景來選擇性的“禁用緩存”內存屏障,又稱內存柵欄,是一個CPU指令,硬件分為下面幾種:

lfence(讀屏障 load Barrier):在讀取指令前插入讀屏障,讓緩存中的數據失效,重新從主內存加載數據,保證數據是最新的。Sfence(寫屏障 store Barrier):在寫入指令后插入屏障,同步把緩存的數據寫回內存,保證其數據立即對其他緩存可見。Mfence(全能屏障):擁有讀屏障和寫屏障的功能。Lock 前綴指令:Lock不是一種內存屏障,但是它能完成類似內存屏障的功能。Lock會對CPU總線和高速緩存加鎖,可以理解為CPU指令級的一種鎖。它后面可以跟ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCH8B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD, and XCHG等指令。

注意:不同的硬件緩存一致性協議和內存屏障可能不同

總結

隨著計算機高速發展,CPU 技術遠超過內存技術,所以多級緩存被使用,解決了內存和 cpu 的讀寫速度問題,隨著多線程的發展,緩存一致性問題油然而生,好在可以通過緩存一致性協議來解決,比較出名的緩存一致性協議是MESI,MESI協議的引入,微微降低了 cpu 的速度。

為了更好的壓榨 cpu 的性能,于是Store Bufferes 概念被引入,將 cpu 寫入主存從同步阻塞變為異步,大大提高了 cpu 執行效率

指令重排序問題預期而至,這時候祭出終極武器:內存屏障指令,在代碼里面禁用緩存。

至此,計算機發展中遇到的問題都一一解決,而這一系列問題解決方案,都是內存模型規范的。

內存模型就是為了解決計算機發展中遇到的緩存一致性、處理器優化和指令重排、并發編程等問題的一系列規范,他定義了共享內存系統中多線程程序讀寫操作行為的規范,通過這些規則來規范對內存的讀寫操作,從而保證指令執行的正確性。

責任編輯:武曉燕 來源: 碼農本農
相關推薦

2023-10-27 07:47:58

Java語言順序性

2023-11-05 12:05:35

JVM內存

2020-11-13 08:42:24

Synchronize

2020-12-11 07:32:45

編程ThreadLocalJava

2022-10-12 07:53:46

并發編程同步工具

2022-03-30 15:25:28

鏈接過程計算機系統程序

2024-05-24 14:35:49

2024-12-31 09:00:12

Java線程狀態

2019-06-25 10:32:19

UDP編程通信

2015-03-24 13:28:52

Java Java Strin內存模型

2022-06-22 08:02:11

CPU操作系統Java

2020-07-02 08:17:12

存儲IO

2021-07-26 07:47:37

無鎖編程CPU

2021-09-08 17:42:45

JVM內存模型

2024-03-19 14:14:27

線程開發

2023-09-19 22:47:39

Java內存

2020-11-04 15:35:13

Golang內存程序員

2013-06-20 10:25:56

2020-06-01 21:07:33

C11C++11內存

2018-06-12 08:53:38

AI內存系統
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 黄色片视频免费 | 午夜激情影院 | 欧美精品综合在线 | 久久9视频| 国产日韩欧美 | 中文字幕视频三区 | 久久久久久免费毛片精品 | 这里只有精品99re | 色综合久久久 | 亚洲在线一区 | 成人高清视频在线观看 | 日本激情视频中文字幕 | 五月天婷婷久久 | 91在线观看免费视频 | 毛片在线视频 | 久久精品一区二区三区四区 | 日韩精品免费看 | 久久久一区二区三区四区 | 国产日韩欧美一区二区 | 亚洲精品电影网在线观看 | 天天干干 | 99久久视频 | 国产成人精品一区二区三区四区 | 日韩国产中文字幕 | 久久久久久电影 | 特级毛片爽www免费版 | 亚洲黄色一区二区三区 | 国产激情视频网址 | 91福利网址 | 看亚洲a级一级毛片 | 日韩www| 你懂的在线视频播放 | 玖玖综合在线 | 欧美不卡 | 国产精品69毛片高清亚洲 | 色播视频在线观看 | 国产视频一区二区在线观看 | 日韩精品在线网站 | 亚洲国产欧美一区二区三区久久 | 电影午夜精品一区二区三区 | 91色网站|