引言
如果你有組裝電腦的經驗、或者正在從事記憶體相關的開發工作,相信一定會經常接觸到 DIMM (Dual Inline Memory Module) 的各種單位名詞:包括例如 Channel、Rank、Chip、Bank 等等。這篇文章,我們將會先從 Byte Addressing 說起,再解釋 SIMM 和 DIMM 的架構變化。最後,我們會詳細了解各種 DIMM 中常見的單位。
---
Byte Addressing
首先,有一個基本概念:現時大部分電腦中的記憶體地址,都以 1-byte (8-bits) 作為單位的。也就是說,每一個 byte 都會有自己的記憶體地址。這一點,跟 C Programming 中的 char array 操作方法一致:
例如:有一個 C char array:
char arr[] = { 'a', 'b', 'c', 'd' };
由於 'a' 和 'b' 都是完整的 8-bit (1-byte),所以,它們都會有自己的記憶體地址:
printf("%p\n", &arr[0]); // example output: 0x00FC00000
printf("%p\n", &arr[1]); // example output: 0x00FC00001
printf("%p\n", &arr[2]); // example output: 0x00FC00002
printf("%p\n", &arr[3]); // example output: 0x00FC00003
---
SIMM (Single Inline Memory Module)
大部分電腦及程式,都是以上述的 byte-addressing 為基礎,所以,當我們向 DRAM 查詢某個地址的資料(例如 0x00FC00001),我們只需期待記憶體能在一個 clock cycle 返回一個 byte 的內容(例如 'b')就好。初代的設計,正正是以這種方向作考慮,所以第一代的 SIMM RAM,都只能提供 8-bit 的資料頻寛。也就是說,一塊記憶體模塊,只能在一個 clock cycle 下傳輸 8-bit 的資料。
及後,人們開始注意到:電腦程式在大部分情況下,都會符合局部性原則(Principle Of Locality)。舉例來說:以上的 C 例子,開發者通常不止會用到 'a',還會經常用 loop 把 'b', 'c', 'd' 等等這些鄰近的變數都讀起來。所以,SIMM 的第二代設計,也開始容許 CPU 同一個 clock cycle 下存取多個鄰近的 bytes(也就是 Burst Read)。當時,第二代 SIMM 能提供 32-bit 的資料頻寛。也就是說,一個記憶體模塊,能在一個 clock cycle 下傳輸 32-bit (4 bytes) 的資料。
---
DIMM (Double Inline Memory Module)
由於 CPU 發展不斷加快,記憶體的密度及容量也越來越高,廠商開始覺得 32-bit 不太夠用。所以,它們又推出了 Double 版的 SIMM(也就是 DIMM),把資料頻寛翻倍成 64-bit。換句話說,現時一塊 DIMM 記憶體模塊,能在一個 clock cycle 下傳輸 64-bit (8 bytes) 的資料。
想像一下,如果一塊記憶體模組,想要在一個 clock cycle 下返回 64 bit,理論上,它就需要有 64 個儲存器同時運作。那麼,這些儲存器,到底是如何排列的呢?
---
Row / Column
簡單來說,DRAM 是由電容所組成的揮發性儲存裝置 (Volatile Memory)。每一顆電容,都可以儲存一個 bit 的資料(高電壓 = 1,低電壓 = 0),而且需要定期充電更新。而它們的排列方式,是以 Row(行)和 Column(列)進行的。
假設現在有一個 8 * 8 的矩陣,裏面放著總共 64 個電容,我們就可以用 6 個 bit(3 行,3 列,2^6 = 64)去定址它們。圖中紅色的一個電容,我們可以用 R=5 (101), C=3 (011)(也就是 101 011)表示:
---
Bank
文章開始的時候,我們提到:由於大部分電腦均採用 Byte Addressing,所以每一個地址,都應該要指向 8-bits 的內容,而非 1-bit。所以,記憶體應該要儲存 8 bits,而非只有 1 bit。要解決這個問題,最簡單的做法,就是把同一個矩陣複製八份。這樣,一個地址,就能同時回傳 8 個 bits。這種排列方法,我們稱為一個 Bank:
▲ 在這個例子中,如果我們向八個矩陣都發出相同的 Row/Column 指令,我們便能同時得到 8 個 bit,也就是一個 byte 的內容。
---
Chip
Chip 就是指一塊黑色的 memory chip。通常,基於效能等種種原因,一個 memory chip 內會有多於一個 bank,而每個 Bank 均可以完全獨立運作。例如下圖的 memory chip 所示,它一共有 8 個 banks。這樣,當用戶正在存取 Bank 0 的時候,Bank 1-7 就可以忙其他事情(例如預先存取 Prefetch、更新電容等等):
---
Rank
從以上例子所見,每一顆 memory chip 都能在同一個 cycle 中返回 8 bits(1 byte)。但是,由於 DIMM 能支援在同一個 cycle 內返回 64 bits(8 bytes)的資料,如果我們只採用一顆 memory chip,理論上,我們就只能用到 8 bit / 64 bit = 12.5% 的頻寛,十分浪費。有見及此,DIMM 會把八顆 8-bit 的 memory chip 並聯在一起(或者四顆 16-bit 的 memory chip,視乎情況),並以 Burst Mode 等方法,令八顆 memory chip 能同時進行讀寫。這種排列,則稱為一個 Rank:
普遍而言,一塊單邊 DIMM 會有 8 顆 memory chip。如果每一粒 memory chip 的頻寬均為 8-bit,那麼 8 顆同時運作,就有 64 bits,就代表一個 Rank,這種就稱為 single rank。
有些 DIMM 的 memory chip 為 16 bit,那麼四顆就已經 64 bit (16 bits * 4 = 64 bits)。這種情況下,擁有八顆就稱為 dual rank。還有一種雙面 DIMM,計算方法也大同小異。
---
Channel
Memory Controller 的數量,決定了 memory channel 的數量。大部分情況下,一個 CPU 可以有一個或多個 memory controller,而每一個 memory controller 則只能有一個 channel。每一個 channel 也只能同時讀寫同一個 Rank 的 memory chip。
例如有一個 CPU,它只有一個 memory controller,而該 memory controller 也只支援一個 channel。如果該主機插上了兩條 single rank 的 DIMM,它也只能同時對其中一條 DIMM 進行讀寫。
又例如有一個 CPU,它有兩個 memory controller,所以它能同時用到兩個 channel(dual channel)。如果該主機插上了兩條 single rank 的 DIMM,它就能同時對兩條 DIMM 進行讀寫。
理論上,如果 CPU 的 memory controller 支援兩個 channel,CPU 就能在一個 clock cycle 讀取 64 bits * 2 = 128 bits 的資料。但如果只有一個 channel,它就只能在一個 clock cycle 讀取 64 bits 的資料。所以,同一個資料量,在理想的情況下,dual channel 能比 single channel 節省一半的存取時間。
當然,現實情況下,資料擺放的位置不一、程式設計造成的瓶頸等,都會令實際速度有所折扣。所以,現實中的 dual channel 並沒有想像中那麼快。
▲ 從以上圖片可見,一個 CPU 可以支援兩個(或以上)的 channel,而每一個 channel 可以支援多個 DIMM 模塊。不過,在同一個 channel 下,只有一個 rank 的 memory chips 能被同時讀取。
---
近年發展
隨著近年 DDR4 / DDR5 等高速記憶體開始普及,廠商也積極引入更多新的技術,以便進行更多的並行運算(parallelism)和更低的延遲(例如 Group Memory Banks 等等)。最經典的例子則是 DDR (Double Data Rate):DDR 相比起 SDR (Single Data Rate),會同時用上 reference clock 的 rising edge 和 falling edge 進行讀寫。但整體而言,記憶體的基本架構仍然不變。
---
參考
[1] Random Access Memory - https://www.youtube.com/playlist?list=PLTd6ceoshpreE_xQfQ-akUMU1sEtthFdB
[2] 圖解RAM結構與原理,系統記憶體的Channel、Chip與Bank - https://www.techbang.com/posts/18381-from-the-channel-to-address-computer-main-memory-structures-to-understand