按照在計算機中作用的不同,存儲器分為緩沖存儲器、主存、輔助存儲器。按照存儲信息的材料或介質可以分為磁帶存儲器.磁盤存儲器、半導體存儲器、光盤存儲器。
理想狀态中,我們将存儲器系統視為一個線性字節數組,CPU能在常數時間内訪問每個存儲器位置。但實際上存儲器系統(Memory System)是一個具有不同容量、成本和訪問時間的存儲設備的層次結構,分别具有以下幾部分:
上方存儲器作為下方存儲器的緩存,速度更快、容量更小。
存儲器的層次結構之所以有效,是因為程序具有局部性(Locality)的基本屬性,傾向于不斷訪問相同的數據項集合,或者傾向于訪問相鄰的數據項集合。我們希望程序能具有更好的局部性,使得數據項存儲在較高層次的存儲器中,這樣程序就會傾向于從存儲器結構中較高層次訪問數據項,運行會更快。
1 存儲技術1.1 随機訪問存儲器随機訪問存儲器(Random-Access Memory,RAM)根據存儲單元實現方式可以分為兩類:靜态的RAM(SRAM)和動态的RAM(DRAM)。
1.1.1 SRAM
由于SRAM存取速度較快,隻要供電就會保持不變,對光和電噪音等幹擾不敏感,但是每位的存儲需要6個晶體管,使得造價較為昂貴,且密集度低,使其适合作為小容量高速的高速緩存存儲器。
1.1.2 DRAM我們可以将w個DRAM單元組成一個超單元(Supercell),使得一個超單元就能存儲w位的信息,并且将d個超單元組合在一個構成一個 d \times wd×w DRAM芯片,能夠存儲dw位信息,并且能對每個超單元進行尋址。并且為了降低地址引腳的數量,我們可以将d個超單元組織成r行、c列的陣列形式。
如上圖所示,我們有一個16 \times 816×8的DRAM芯片,其中包含16個超單元,每個超單元由8個DRAM單元組成,使得每個超單元能存儲8位信息。并且16個超單元被組成4行4列的陣列形式。由一個内存控制器(Memory Controller)通過addr引腳和data引腳将控制DRAM芯片數據的傳入和傳出,比如想要獲得(2,1)處超單元的數據
注意:
為了一次性能訪問更多的數據,可以将多個DRAM芯片封裝到一個内存模塊(Memory Module)中,将其紮到主闆的擴展槽中。
如上圖所示是封裝了8個 8M \times 88M×8 DRAM芯片的内存模塊,每個DRAM芯片負責8位數據,這樣一次能對64位字進行讀寫。比如想要獲得地址A處的字:
為了進一步擴大存儲能力,可以将多個内存模塊連接到内存控制器,能夠聚合成主存。當内存控制器想要讀取地址A處的字時,會先找到包含地址A的内存模塊k,然後根據上述步驟得到對應的字。
而基于傳統的DRAM單元,可以做一些優化來提高訪問基本DRAM單元的速度:
從更高層面來看,數據流是通過稱為總線(Bus)的共享電子電路在處理器和DRAM主存之間傳遞數據的。總線是一組并行的導線,能夠攜帶地址、數據和控制信号,也可以将數據和地址信号使用相同的導線。
如上圖所示是一個連接CPU和DRAM主存的總線結構。其中I/O橋接器(I/O Bridge)芯片組包括内存控制器,能夠将系統總線的電子信号和内存總線的電子信号互相翻譯,也能将系統總線和内存總線連接到I/O總線。
當從内存加載數據到寄存器中:
當将寄存器中的數據保存到内存中:
這裡的讀事務和寫事務統稱為總線事務(Bus Transaction)。
1.1.3 非易失性存儲器之前介紹的DRAM和SRAM在斷電時都會丢失數據,所以是易失的(Volatile),而非易失性存儲器(Nonvolatile Memory)即使斷電後,也會保存信息,該類存儲器稱為隻讀存儲器(Read-Only Memory,ROM),但是現在ROM中有的類型既可以讀也可以寫了,可以根據ROM能夠重編程的次數以及對它們進行重編程所用的機制進行區分,包括:
存儲在ROM設備中的程序稱為固件(Firmware),包括BIOS、磁盤控制器、網卡、圖形加速器和安全子系統等。當計算機系統通電後,會運行存儲在ROM中的固件。
1.2 磁盤存儲磁盤(Disk)是被用來保存大量數據的存儲設備,但是讀信息的速度比DRAM慢10萬倍,比SRAM慢100萬倍。
如上圖所示是一個磁盤的構造。磁盤是由多個疊放在一起的盤片(Platter)構成,每個盤片有兩個覆蓋着磁性記錄材料的表面(Surface)。每個表面由一組稱為磁道(Track)的同心圓組成,每個磁道被劃分為若幹扇區(Sector),每個扇區包含相同數量的數據位(通常為512位)作為讀寫數據的基本單位。扇區之間通過間隙(Gap)分隔開來,間隙不保存數據信息,隻用來表示扇區的格式化位。通常會使用柱面(Cylinder)來描述不同表面上相同磁道的集合,比如柱面k就是6個表面上磁道k的集合。盤片中央會有一個可以旋轉的主軸(Spindle),使得盤片以固定的旋轉速率(Rotational Rate)旋轉,單位通常為RPM(Revolution Per Minute)。
将磁盤能記錄的最大位數稱為最大容量(容量),主要由以下方面決定:
磁盤容量的計算公式為:
在面密度較低時,每個磁道都被分成了相同的扇區,所以能夠劃分的扇區數由最内側磁道能記錄的扇區數決定,這就使得外側的磁道具有很多間隙。現代大容量磁盤采用多區記錄(Multiple Zone Recording)技術,将一組連續的柱面劃分成一個區,在同一個區中,每個柱面的每條磁道都有相同數量的扇區,由該區中最内側的磁道決定,由此使得外側的區能劃分成更多的扇區。
如上圖所示,磁盤通過一個連接在傳動臂(Actuator Arm)上的讀/寫頭(Read/Write Head)來進行讀寫,對于有多個盤面的磁盤,會用多個位于同一柱面上的垂直排列的讀/寫頭。對于扇區的訪問時間(Access Time)由以下幾部分構成:
可以發現:尋道時間和旋轉時間是主要影響部分,并且兩者大緻相等,通常可以尋道時間乘2來估計訪問時間。
由于磁盤構造的複雜性,現代磁盤将其抽象為B個扇區大小的邏輯塊序列,編号為0,1,...,B-1,通過磁盤中的磁盤控制器來維護邏輯塊号和實際扇區之間的映射關系。為此需要通過磁盤控制器對磁盤進行格式化:
當從磁盤讀取數據到主存,需要以下步驟:
如上圖所示是一個總線結構實例。對于像圖形卡、鼠标、鍵盤、監視器這類輸入/輸出設備,都是通過I/O總線連接到CPU和主存的,比如Intel的外圍設備互聯(Peripheral Component Interconnect,PCI)總線,在PCI模型中,系統中所有的設備共享總線,一個時刻隻能有一台設備訪問這些線路,目前PCI總線已被PCEe總線取代了。雖然I/O總線比系統總線和内存總線慢,但是能容納種類繁多的第三方I/O設備。
注意:系統總線和内存總線是與CPU相關的,而PCI總線這樣的I/O總線被設計成與底層CPU無關。
CPU會在地址空間中保留一塊地址用于與I/O設備通信,每個地址稱為I/O端口(I/O Port),而連接到總線的設備會被映射到一個或多個端口,則處理器可通過端口地址來訪問該I/O設備,該技術稱為内存映射I/O(Memory-mapped I/O)。
假設磁盤控制器映射到端口0xa0,探讨磁盤的讀取過程:
固态硬盤(Solid State Disk,SSD)是一種基于閃存的存儲技術,插在I/O總線上标準硬盤插槽(通常為USB或SATA),處于磁盤和DRAM存儲器的中間點。從CPU的角度來看,SSD與磁盤完全相同,有相同的接口和包裝。它由閃存和閃存翻譯層(Flash Translation Layer)組成
當對頁進行寫操作時,首先需要先對該頁所處的整個塊進行擦除。
以上是Intel SSD 730的性能,IOPS是每秒I/O操作數,吞吐量數量基于4KB塊的讀寫。我們可以發現随機寫操作較慢,這是因為:
塊的擦除次數是有限的,當塊磨損後,就不能再使用了,閃存翻譯層中的平均磨損(Wear Leveling)邏輯會試圖将擦除平均到所有塊中,來最大化每個塊的壽命。
SSD的優缺點:
具有以下重要思想:
從上一圖中可看出,DRAM主存和磁盤的性能滞後于CPU性能,訪問時間比單個處理器的周期時間慢很多,而SRAM的性能雖然也滞後于CPU性能,但是還保持增長,所以現代計算機會使用基于SRAM的高速緩存,來彌補CPU和内存之間的差距。
2 局部性具有良好局部性(Locality)的程序,會傾向于引用最近引用過的數據項本身,或者引用最近引用過的數據項周圍的數據項。局部性主要具有兩種形式:
從硬件到操作系統,再到應用程序,都利用了局部性。
有良好局部性的程序比局部性較差的程序運行更快。
想要分析一個程序的局部性是否好,可以依次分析程序中的每個變量,然後根據所有變量的時間局部性和空間局部性來總和判斷程序的局部性。
例1:
分析上述程序的局部性。對于變量sum,每一輪叠代都會引用一次,所以sum具有好的時間局部性,而sum是标量,所以沒有空間局部性。對于變量v,其數據在内存中的分布如圖b中所示,每一輪叠代都是引用不同的數據項,所以時間局部性較差,但是會按照内存存儲的順序依次引用數據項,所以空間局部性較好。 綜合來說,該程序具有較好的局部性。
并且由于程序是以指令形式保存在内存中的,而CPU會從内存中讀取指令,所以也可以考慮取指的局部性。由于該循環體内的指令是順序保存在内存中的,而CPU會按順序進行取指,所以具有良好的空間局部性,并且叠代多次會反複讀取相同的指令,所以具有良好的時間局部性,所以該程序的局部性較好。
對于一個向量,如果每一輪引用的數據項之間在内存空間中相隔k,則稱該程序具有步長為k的引用模式(Stride-k Reference Pattern)。步長k越大,則每一輪引用的數據在内存中間隔很大,則空間局部性越差。
例2:
對于以上代碼,變量sum的時間局部性較好且不具有空間局部性,對于二維數組變量v,在内存中是按照行優先存儲的,而代碼中也是按照行優順序進行應用的,所以變量v具有步長為1的引用模式,所以具有較好的空間局部性,而時間局部性較差。總體來說,該程序具有良好的局部性。
例3:
上述代碼将變量v的引用順序變為了列優先,則根據v的内存存儲形式,變量v具有步長為N的引用模式,則時間局部性較差,且空間局部性也較差。總體來說,該程序的局部性較差。
例4:
我們需要判斷以上三個函數的局部性。首先根據結構體的定義可以得到結構體數組在内存中的存儲形式如下所示
則clear1函數的步長為1,具有良好的空間局部性;而clear2函數會在結構體中不同的字段中反複跳躍,空間局部性相對clear1差一些;而clear3函數會在相鄰兩個結構體中反複跳躍,空間局部性相比clear2更差。
總體而言:
通過上面兩節,我們可以得到存儲技術和軟件的基本屬性:
兩者存在一定的互補,由此可以得到一種組織存儲器系統的方法,存儲器層次結構(Memory Hierarchy)。
如上圖所示是一種經典的存儲器層次結構,會使用基于SRAM的高速緩存存儲器來解決CPU和DRAM主存之間的鴻溝,通常還可以在DRAM主存和本地磁盤之間添加一層SSD,來彌補兩者之間的差距。通常還可以在本地磁盤下方添加一個本地磁帶,提供成本更低的存儲。
高速緩存(Cache)是一個小而快速的存儲設備,用來作為存儲在更大更慢設備中的數據對象的緩沖區域。而使用高速緩存的過程稱為緩存(Caching)。
存儲器層次結構的中心思想是讓層次結構中的每一層來緩存低一層的數據對象,将第k層的更快更小的存儲設備作為第k 1層的更大更慢的存儲設備的緩存。
該結構之所以有效,是因為程序的局部性原理。相比于第k 1層的數據,程序會傾向于訪問存儲在第k層的數據。如果我們訪問第k 1層存儲的數據,我們會将其拷貝到第k層,因為根據局部性原理我們很有可能将再次訪問該數據,由此我們就能以第k層的訪問速度來訪問數據。而且因為我們不經常訪問第k 1層的數據,我們就可以使用速度更慢且更便宜的存儲設備。
上圖展示的是存儲器層次結構的基本緩存原理。每一層存儲器都會被劃分成連續的數據對象組塊,稱為塊(Block),每個塊都有一個唯一的地址或名字,并且通常塊的大小都是固定的。第k層作為第k 1層的緩存,數據會以塊大小作為傳送單元(Transfer Unit)在第k層和第k 1層之間來回賦值,使得第k層保存第k 1層塊的一個子集的副本。通常存儲器層次結構中較低層的設備的訪問時間較長,所以較低層中會使用較大的塊。
3.1 緩存命中當程序需要第k 1層的某個數據對象d時,會先在第k層的塊中搜索d,如果d剛好緩存在第k層中,則成為緩存命中(Cache Hit),則該程序會直接從第k層中讀取d。根據存儲器層次結構,可以知道第k層的讀取速度更快,因此緩存命中會使得程序更快。
3.2 緩存不命中如果第k層沒有緩存數據對象d,則稱為緩存不命中(Cache Miss),則會從第k 1層中取出包含d的塊,然後第k層的緩存會執行某個放置策略(Placement Policy)來決定該塊要保存在第k層的什麼位置
随機放置塊會使得定位起來代價很高。
比較特殊的情況是第k層的緩存為空,那麼對于任意的數據對象的訪問都會不命中。空的緩存稱為冷緩存(Cold Cache),該不命中稱為強制性不命中(Compulsory Miss)或冷不命中(Cold Miss)。
程序通常會按照一系列階段來運行,每個階段會訪問緩存塊的某個相對穩定不變的集合,則該集合稱為工作集(Working Set),如果工作集大小超過緩存大小,則緩存會出現容量不命中(Capacity Miss),這是由緩存太小導緻的。
3.3 緩存管理對于每層存儲器,都會有某種形式的邏輯來管理緩存:将緩存劃分成塊、在不同層之間傳遞塊、判斷緩存是否命中并進行處理。
通過以上内容,就能解釋局部性好的程序的優勢:
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!