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