內(nèi)存隨機(jī)也比順序訪問(wèn)慢,帶你深入理解內(nèi)存IO過(guò)程
問(wèn)題1: 內(nèi)存訪問(wèn)一次延時(shí)到底是多少?你是否會(huì)進(jìn)行大概的估算?
在我們的這個(gè)故事中,你是故事的主角。你有一所房子,房子里有一個(gè)仆人,他每天幫你處理各種各樣的圖書數(shù)據(jù)。但是北京房?jī)r(jià)太貴,所以你的這個(gè)房子很小,只能放的下64本書。你家的馬路對(duì)面,就是北京圖書館(你家房子雖然小但是地段還不錯(cuò)),你所需要的所有的圖書在那里都可以找到。圖書館有個(gè)管理員,他負(fù)責(zé)幫你把你想要的書找出來(lái)。
場(chǎng)景2:
場(chǎng)景3:
這四個(gè)場(chǎng)景里,我覺(jué)得你一定發(fā)現(xiàn)了不同情形下耗時(shí)的差異。
-
場(chǎng)景1和場(chǎng)景4花費(fèi)的時(shí)間最多。因?yàn)閳D書管理員需要花時(shí)間坐電梯找樓層,需要花時(shí)間在樓內(nèi)找書。
-
場(chǎng)景3次之,因?yàn)閳D書管理員直接就在樓層內(nèi),只需要花時(shí)間在樓內(nèi)找書既可
-
場(chǎng)景2最快,因?yàn)橹恍枰腿藥湍銖目蛷d拿過(guò)來(lái)就好,連馬路都不需要過(guò)。
之所以編造這么一個(gè)例子,是因?yàn)閮?nèi)存的工作方式和它太像了。接下來(lái)我們進(jìn)入內(nèi)存的實(shí)際分析。
2內(nèi)存物理結(jié)構(gòu)在《帶你理解內(nèi)存對(duì)齊最底層原理》中我們了解了內(nèi)存顆粒的物理構(gòu)造以及IO過(guò)程,今天我們?cè)賮?lái)復(fù)習(xí)一下。
內(nèi)存是由chip構(gòu)成。每個(gè)chip內(nèi)部,是由8個(gè)bank組成的。其構(gòu)造如下圖:
圖3 bank內(nèi)部物理結(jié)構(gòu)
根據(jù)上面幾張圖我們可以大致了解內(nèi)存的IO過(guò)程,在這個(gè)過(guò)程中每一步操作之間都有一些延遲,讓我們來(lái)繼續(xù)了解這些延遲。
3內(nèi)存IO延遲在《從DDR發(fā)展到DDR4,內(nèi)存核心頻率指標(biāo)其實(shí)基本上就沒(méi)太大的進(jìn)步》里我們提到內(nèi)存的延遲很大程度是受核心頻率制約的,你也應(yīng)該記得我們提到了內(nèi)存延遲一般是通過(guò)CL-tRCD-tRP-tRAS四個(gè)參數(shù)來(lái)標(biāo)識(shí)的。我們今天來(lái)詳細(xì)理解一下這四個(gè)參數(shù)的含義:
-
CL(Column Address Latency):發(fā)送一個(gè)列地址到內(nèi)存與數(shù)據(jù)開始響應(yīng)之間的周期數(shù)
-
tRCD(Row Address to Column Address Delay):打開一行內(nèi)存并訪問(wèn)其中的列所需的最小時(shí)鐘周期數(shù)
-
tRP(Row Precharge Time):發(fā)出預(yù)充電命令與打開下一行之間所需的最小時(shí)鐘周期數(shù)。
-
tRAS(Row Active Time):行活動(dòng)命令與發(fā)出預(yù)充電命令之間所需的最小時(shí)鐘周期數(shù)。也就是對(duì)下一次預(yù)充電時(shí)間進(jìn)行限制。
要注意除了CL是固定周期數(shù)以外,其它的三個(gè)都是最小周期。另外上面的參數(shù)都是以時(shí)鐘周期為單位的。因?yàn)楝F(xiàn)代的內(nèi)存都是一個(gè)時(shí)鐘周期上下沿分別各傳輸一次數(shù)據(jù),所以用Speed/2就可以得出,例如筆者的機(jī)器的Speed是1066MHz,則時(shí)鐘周期為533MHz。你自己的機(jī)器可以通過(guò)dmidecode命令查看:
# dmidecode | grep -P -A16 "Memory Device" Memory Device ...... Speed: 1067 MHz ......
和“圖書管理員”類似,內(nèi)存芯片也有類似的工作場(chǎng)景:
場(chǎng)景1: 你的進(jìn)程需要內(nèi)存地址0x0000為的一個(gè)字節(jié)的數(shù)據(jù),CPU這時(shí)候向內(nèi)存控制器發(fā)出請(qǐng)求,內(nèi)存控制器進(jìn)行行地址的預(yù)充電,需要等待tRP個(gè)時(shí)鐘周期。再發(fā)出打開一行內(nèi)存的命令,又需要等待tRCD個(gè)時(shí)鐘周期。接著發(fā)送列地址,再等待CL個(gè)周期。最終將0x0000-0x0007的數(shù)據(jù)全部返回給了CPU。CPU把這些數(shù)據(jù)放入到了自己的cache里,并幫你開始對(duì)0x0000的數(shù)據(jù)進(jìn)行運(yùn)算。 場(chǎng)景2: 你的進(jìn)程需要內(nèi)存地址0x0003的一個(gè)字節(jié)數(shù)據(jù),CPU發(fā)現(xiàn)發(fā)現(xiàn)它在自己的cache里存在,直接使用就好了。這個(gè)場(chǎng)景里其實(shí)根本就沒(méi)有內(nèi)存IO發(fā)生。 場(chǎng)景3: 你的進(jìn)程需要內(nèi)存地址0x0008的一個(gè)字節(jié)數(shù)據(jù),CPU的cache并沒(méi)有命中,于是向內(nèi)存控制器請(qǐng)求。內(nèi)存控制器發(fā)現(xiàn)行地址和上一次工作的行地址一致,這次只需要發(fā)送列地址后等待CL個(gè)周期,就可以拿到0x0008-0x0015的數(shù)據(jù)并返回給CPU了。 場(chǎng)景4: 你的進(jìn)程需要內(nèi)存地址0xf000的一個(gè)字節(jié)數(shù)據(jù),同樣CPU的cache并不命中,向內(nèi)存控制器請(qǐng)求。內(nèi)存控制器一看(內(nèi)心有些許的郁悶),這次行w地址又變了,得,和場(chǎng)景1一樣。繼續(xù)等待tRP+tRCD+CL個(gè)周期后,才能夠取到數(shù)據(jù)并返回。 實(shí)際的計(jì)算機(jī)的內(nèi)存IO過(guò)程中還需要進(jìn)行邏輯地址和物理地址的轉(zhuǎn)換,這里忽略不表。
4結(jié)論其中場(chǎng)景1和場(chǎng)景4是隨機(jī)IO的情況,場(chǎng)景2無(wú)內(nèi)存IO發(fā)生,場(chǎng)景3是順序IO,。通過(guò)上面的過(guò)程描述我們可以得到結(jié)論。內(nèi)存也存在和磁盤一樣,隨機(jī)IO比順序IO要慢的問(wèn)題。如果行地址同上一次訪問(wèn)的不一致,則需要重新拷貝row buffer,延遲周期需要tRP+tRCD+CL。而如果是順序IO的話(行地址不變),只需要CL個(gè)周期既可完成。
我們接著估算下內(nèi)存的延時(shí),筆者的機(jī)器上的內(nèi)存參數(shù)Speed為1066MHz(通過(guò)dmidecode查得),該值除以2就是時(shí)鐘周期的頻率=1066/2=533Mhz。其延遲周期為7-7-7-24。
-
隨機(jī)IO:這種狀況下需要tRP+tRCD+CL個(gè)時(shí)鐘周期,7+7+7=21個(gè)周期。但是還有個(gè)tRAS的限制,兩次行地址預(yù)充電不得小于24。所以我們得按24來(lái)計(jì)算,24*(1s/533Mhz) = 45ns
-
順序IO:這種狀況下只需要CL個(gè)時(shí)鐘周期 7*(1s/533Mhz)=13ns
5擴(kuò)展,CPU的cache line虛擬內(nèi)存概念因?yàn)閷?duì)于內(nèi)存來(lái)說(shuō),隨機(jī)IO一次開銷比順序IO高好幾倍。所以操作系統(tǒng)在工作的時(shí)候,會(huì)盡量讓內(nèi)存通過(guò)順序IO的方式來(lái)進(jìn)行。做法關(guān)鍵就是Cache Line。當(dāng)CPU發(fā)現(xiàn)緩存不命中的時(shí)候,實(shí)際上從來(lái)不會(huì)向內(nèi)存去請(qǐng)求1個(gè)字節(jié),8個(gè)字節(jié)這種。而是一次性就要64字節(jié),然后放到自己的Cache中存起來(lái)。
用上面的例子來(lái)看,
-
如果隨機(jī)請(qǐng)求8字節(jié):耗時(shí)是45ns
-
如果隨機(jī)請(qǐng)求64字節(jié):耗時(shí)是45+7*13 = 136ns
開銷也沒(méi)貴多少,因?yàn)橹挥械谝粋€(gè)字節(jié)是隨機(jī)IO,后面的7個(gè)字節(jié)都是順序IO。數(shù)據(jù)是8倍,但是IO耗時(shí)只有3倍,而且取出來(lái)的數(shù)據(jù)后面大概率要用,所以計(jì)算機(jī)內(nèi)部就這么搞了,通過(guò)這種方式幫你避免一些隨機(jī)IO!
另外,內(nèi)存也支持burst(突發(fā)傳輸)模式,在這種模式下可以只傳入一次行列地址,就命令內(nèi)存返回該內(nèi)存開頭的連續(xù)字節(jié)數(shù)據(jù),比如64字節(jié)。這種模式下,只有第一次的8字節(jié)需要真正的行列訪問(wèn)延遲,后面的7個(gè)字節(jié)可以直接按內(nèi)存的數(shù)據(jù)頻率給吐出來(lái)。
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!