日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

内存模型 C++ 和Java内存模型

發(fā)布時(shí)間:2024/2/28 c/c++ 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 内存模型 C++ 和Java内存模型 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

內(nèi)存模型一:什么是內(nèi)存模型

縱然工作再忙也應(yīng)該要留下自己思考的時(shí)間,這次我總結(jié)了一下對(duì)于內(nèi)存模型的理解,起因是在公司聽了一場關(guān)于多線程編程的分享會(huì)。首先解釋一下,內(nèi)存模型和對(duì)象模型是不同的。對(duì)象模型說的是一個(gè)對(duì)象是如何被設(shè)計(jì)的,其在內(nèi)存中是如何布局的。而內(nèi)存模型說的是,在多核多線程環(huán)境下,各種不同的CPU是如何以一種統(tǒng)一的方式來與內(nèi)存交互的。

背景知識(shí):CPU的高速緩存

總所周知,CPU和內(nèi)存并不是直接交換數(shù)據(jù)的,它們之間還隔著一個(gè)高速緩存。高速緩存是對(duì)程序員透明的,這意味在編程的時(shí)候是感知不到CPU的緩存的存在的。一般情況下確實(shí)如此,但在,在某些特殊的情形下(多核多線程),就不能忽略緩存的存在了。這其實(shí)是和緩存的設(shè)計(jì)有關(guān)系,一般多處理器下的每個(gè)CPU都有一個(gè)自己的緩存,存儲(chǔ)在這個(gè)緩存的數(shù)據(jù)是其它CPU是無法查看的。

引入問題1:內(nèi)存可見性

問題來了,由于緩存是每個(gè)CPU私有的,那么在多線程環(huán)境下,某個(gè)CPU修改了變量x后保存在本地緩存,對(duì)于其它CPU,何時(shí)才能發(fā)現(xiàn)變量x被修改呢?如何保證其它CPU的緩存中持有的x的值是最新的呢?

由此可見,在多核多線程環(huán)境下,讀寫共享變量要解決的不僅是原子性,還需要保證其內(nèi)存可見性。更糟的是,現(xiàn)代CPU通常在執(zhí)行指令時(shí)會(huì)允許一定程度上的亂序,這使保證在多個(gè)CPU緩存的數(shù)據(jù)一致更是增加了復(fù)雜性。通常方法是通過一個(gè)協(xié)議來保證數(shù)據(jù)在各個(gè)CPU的緩存是一致性,這就是緩存一致性協(xié)議。

關(guān)于緩存一致性簡單的舉個(gè)列子。CPU-0嘗試STORE(更新)變量x,但其發(fā)現(xiàn)其它CPU的緩存也持有這個(gè)x的copy(x此時(shí)為Shared狀態(tài),非單個(gè)CPU獨(dú)占),那么當(dāng)CPU-0在STORE之前,必須通過一個(gè)disable消息,告訴其它CPU所持有的變量x已經(jīng)為臟數(shù)據(jù),是不可用狀態(tài)。其它CPU在收到這個(gè)disable消息后必須回應(yīng)CPU-0一個(gè)ack消息,這時(shí)候CPU-0才能開始STORE變量x。

通過緩存一致性協(xié)議之后,內(nèi)存可見性問題似乎是得以解決了。但是,這里面還隱藏著另外一個(gè)問題:指令亂序!

引入問題2:亂序(memory reorder)

先來解釋一下,亂序,指的是程序指令實(shí)際上執(zhí)行的順序,和我們書寫的指令的順序不一致。亂序分兩種,分別是編譯器的指令重排和CPU的亂序執(zhí)行。本意上亂序是為了優(yōu)化指令執(zhí)行的速度而產(chǎn)生的。并且為了維護(hù)程序原來的語義,編譯器和CPU不會(huì)對(duì)兩個(gè)有數(shù)據(jù)依賴的指令重排(reorder)。這種保護(hù)在單線程的環(huán)境下是可以工作的,但是到了多線程,問題就復(fù)雜了。

舉個(gè)例子,CPU-0將要執(zhí)行兩條指令,分別是:

  • STORE x
  • LOAD y
  • 當(dāng)CPU-0執(zhí)行指令1的時(shí)候,發(fā)現(xiàn)這個(gè)變量x的當(dāng)前狀態(tài)為Shared,這意味著其它CPU也持有了x,因此根據(jù)緩存一致性協(xié)議,CPU-0在修改x之前必須通知其它CPU,直到收到來自其它CPU的ack才會(huì)執(zhí)行真正的修改x。但是,事情沒有這么簡單。現(xiàn)代CPU緩存通常都有一個(gè)Store Buffer,其存在的目的是,先將要Store的變量記下來,注意此時(shí)并不真的執(zhí)行Store操作,然后待時(shí)機(jī)合適的時(shí)候再執(zhí)行實(shí)際的Store。有了這個(gè)Store Buffer,CPU-0在向其它CPU發(fā)出disable消息之后并不是干等著,而是轉(zhuǎn)而執(zhí)行指令2(由于指令1和指令2在CPU-0看來并不存在數(shù)據(jù)依賴)。這樣做效率是有了,但是也帶來了問題。雖然我們?cè)趯懗绦虻臅r(shí)候,是先STORE?x再執(zhí)行LOAD y,但是實(shí)際上CPU卻是先LOAD y再STORE x,這個(gè)便是CPU亂序執(zhí)行(reorder)的一種情況!

    當(dāng)你的程序要求指令1、2有邏輯上的先后順序時(shí),CPU這樣的優(yōu)化就是有問題的。但是,CPU并不知道指令之間蘊(yùn)含著什么樣的邏輯順序,在你告訴它之前,它只是假設(shè)指令之間都沒有邏輯關(guān)聯(lián),并且盡最大的努力優(yōu)化執(zhí)行速度。因此我們需要一種機(jī)制能告訴CPU:這段指令執(zhí)行的順序是不可被重排的!做這種事的就是內(nèi)存屏障(memory barrier)!

    內(nèi)存屏障

    還是上面那個(gè)例子,如果不想指令1、2被CPU重排,程序應(yīng)該這么寫:

  • STORE x
  • WMB (Write memory barrier)
  • LOAD y
  • 通過在STORE x之后加上這個(gè)寫內(nèi)存屏障,就能保證在之后LOAD y指令不會(huì)被重排到STORE x之前了。

    內(nèi)存模型是什么

    前面講了那么多,那么內(nèi)存模型是什么呢?

    首先,殘酷的現(xiàn)實(shí)就是每個(gè)CPU設(shè)計(jì)都是不同的,每個(gè)CPU對(duì)指令亂序的程度也是不一樣的。比較保守的如x86僅會(huì)對(duì)Store Load亂序,但是一些優(yōu)化激進(jìn)的CPU(PS的Power)會(huì)允許更多情況的亂序產(chǎn)生。如果目標(biāo)是寫一個(gè)跨平臺(tái)多線程的程序,那么勢(shì)必要了解每一個(gè)CPU的細(xì)節(jié),來插入確切的、足夠的內(nèi)存屏障來保證程序的正確性。這是多么的不科學(xué)啊!科學(xué)的做法應(yīng)該是,我為一個(gè)抽象的機(jī)器寫一套抽象的程序,然后在不同的平臺(tái)下讓編程語言、編譯器來生成合適的內(nèi)存屏障。因此,我們有了內(nèi)存模型的概念。不同平臺(tái)下的實(shí)現(xiàn)差別被統(tǒng)一的內(nèi)存模型所隱藏,只需要根據(jù)這個(gè)抽象的內(nèi)存模型來編寫程序即可,這便是偉大的抽象...

    因此,在C++11里有了內(nèi)存模型的在之后,我們可以僅通過標(biāo)準(zhǔn)庫就實(shí)現(xiàn)出跨平臺(tái)線程安全的lock free程序(這在C++11之前是做不到的,雖然Java早就有了內(nèi)存模型)。

    總結(jié)

    以上是生活随笔為你收集整理的内存模型 C++ 和Java内存模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。