JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

記憶體內運算與近資料運算

數十年來我們不斷加速處理器,記憶體卻爬得慢,直到搬資料本身成了真正的瓶頸、也成了真正的能源帳單。這篇導覽要問接下來那個激進的問題:與其把資料搬去運算單元,何不把運算搬去資料那裡?

瓶頸從來不是運算——而是那趟路程

在這一級稍早,你已認識了後摩爾時代那道令人不安的算術:電晶體還在小幅縮小,但時脈與功率的便宜紅利已經結束,而快速處理器與慢速記憶體之間的鴻溝——也就是記憶體牆——還在持續拉大。現在我們把這道牆追到它最尖銳的結論。在經典的馮紐曼架構裡,運算住在 CPU、資料住在跨越匯流排的DRAM,而每一條指令、每一筆資料都得擠過那一道門——這就是馮紐曼瓶頸。長久以來我們把它當成天經地義,並打造越來越巧妙的快取去掩蓋它。這篇導覽談的是放棄掩蓋那趟路程,轉而質問:那趟路程到底需不需要發生?

為什麼搬資料會佔主導?兩個數字道出了整個故事。第一是頻寬:一個 CPU 核心每秒能咀嚼的數字,遠多於記憶體通道能送達的量,所以對資料飢渴的工作來說,處理器大部分時間在等,而不是在算。第二、也更具決定性的是能量:在現代晶片上,從晶片外的 DRAM 取一個字的能量,大約是你接著對它做的那次算術的一百倍量級。再讀一遍——那次加法幾乎是免費的;去取運算元的那趟通勤才是耗電的元凶。當資料很龐大而數學很簡單時——掃描資料庫、加總一個巨大向量、相乘稀疏矩陣——你光是為了讓位元組透過那一道門來回穿梭,就付出了昂貴的能量與時間。

近資料運算:把工人挪近一點

這個想法溫和的那一半是近資料運算:別把記憶體搬進處理器,只要把一些適度的運算放在資料附近,讓資料走更短的距離——或者根本不離開晶片。把它想成一個關於工人得走多遠的光譜。一端是:資料坐在 DRAM 裡,工人是遠處的 CPU。近資料運算把工人沿著光譜往資料那端滑:也許是記憶體控制器旁的一個小型加速器,也許是儲存裝置本身內部的邏輯。

一個具體、已出貨的例子是運算式儲存:一個內部帶有小型處理器、屬於 SSD 等級的裝置。假設你磁碟上有十億筆記錄,卻只想要符合某個篩選條件的那幾千筆。傳統路徑會把全部十億筆記錄透過匯流排拖上來進到 CPU,而其中幾乎全部立刻被丟棄——為了幾乎留不下的工作,付出一趟巨大的通勤。有了近資料篩選,你把那個述詞送下去到裝置,它的小處理器就在記錄原本所在之處掃描,而只有符合的那幾千筆才走回程。你把一個小程式搬去了資料那裡,而不是把汪洋般的資料搬去程式那裡。當篩選越具選擇性——當你取回來的東西大部分本來都會被丟掉時——這個好處就越大。

記憶體內運算:在位元本就所在之處運算

更大膽的那一半是記憶體內運算(PIM):把真正的算術放進記憶體裡面,這樣對於合適的運算,資料根本不離開它被儲存的那塊晶片。DRAM 是一格格細胞排成的網格,組織成許多銀行(bank)與列;要讀取時,晶片會一次激活一整列、把它送進一個很寬的內部緩衝區——同時數千個位元——然後才把其中窄窄的一片透過接腳擠出去給系統其他部分。接腳就是那個瓶頸。PIM 背後的洞見是:在晶片內部,資料極寬、存取也便宜;昂貴的只是透過那些窄接腳出去的那趟旅程。那麼,何不在那筆很寬的內部資料上、就在那裡、趁它還沒到達接腳之前,先做掉一部分工作呢?

  Where does a sum of a billion numbers happen?

  von Neumann (classic):                 PIM (compute near the array):
  +-----------+      narrow bus          +--------------------------+
  |   DRAM    | =====================>    |  DRAM bank               |
  |  (data)   |   every number must      |   row buffer: 1000s wide  |
  +-----------+   cross the pins, one    |   +-----+ tiny ALU adds   |
        |         slice at a time        |   | ALU | the row locally  |
        v                                |   +-----+                  |
  +-----------+                          +--------------------------+
  |    CPU    |  adds them (cheap math,            |
  |  one ALU  |  but it WAITED forever)            v   only the small
  +-----------+                            partial sums leave the chip

  Same answer. The PIM version barely uses the expensive doorway.
馮紐曼路徑逼每一個運算元都擠過窄接腳、送到一個遙遠的 ALU;PIM 則在位元所在之處、就那筆很寬的內部列上做便宜又平行的工作,只把小小的結果送出去。

PIM 有幾種風味,而這裡誠實很重要,因為這個詞被用得很鬆。近期最務實的形式是近記憶體運算:把小而普通的 ALU 放在堆疊記憶體封裝的邏輯層裡(回想上一篇導覽,3D 堆疊與一個矽中介層如何讓一塊邏輯晶粒就坐在 DRAM 晶粒底下、共用一條又粗又短的連線,像高頻寬記憶體那樣)。一個更激進的形式是DRAM 內運算,它利用記憶體陣列的類比行為——同時激活兩三列,讓位元線自然算出一個逐位元的 AND/OR,把儲存陣列本身變成一個又寬、又笨、卻巨量平行的算術引擎。前者是你不久就能買到的工程;後者大多還在研究階段,帶有真實的精度與可靠度的告誡。

為什麼是現在,又為什麼很難

在記憶體裡運算這個想法已經有幾十年歷史——那為什麼它突然認真了起來?三股力量匯流了,而它們你爬這一級時都已認識。第一,把邏輯與 DRAM 實際擺在一起的技術終於存在、也負擔得起了,這要歸功於先進封裝——封裝那篇的堆疊、中介層,以及小晶片。第二,如今驅動一切的工作負載,正是 PIM 最能幫上忙的那種記憶體受限、低強度的工作:機器學習推論、推薦系統、圖分析、資料庫掃描,全都搬動汪洋般的資料、每位元組卻只做一點點數學。第三,由於Dennard 縮放已死,能量成了每塊晶片的硬限制,而既然資料通勤主宰了那份能量,攻擊那趟通勤就成了架構師現在最具槓桿的事。

現在來談誠實的告誡,因為 PIM 是真的難,而那個喘著氣的版本把它吹過了頭。(1)它不是通用 CPU。記憶體附近那些小引擎擅長簡單、規則、平行的運算——加、比較、遮罩、累加——卻不擅長充滿分支、序列化、不規則的程式碼,就像GPU一樣。工作負載必須合得來。(2)DRAM 是用一套為廉價密集儲存而調校的製程製造的,不是為快速邏輯,所以在它裡面造的邏輯,比普通處理器晶粒上的邏輯更慢、更笨拙——這裡有真實的製造張力。(3)最難的部分很少是矽;而是軟體。我們有數十年的編譯器、語言與作業系統,全都建立在「運算與記憶體是分開的地方」這個馮紐曼假設上。決定哪些要卸載下去、讓 CPU 的快取與在記憶體內部算出的結果保持一致、並把這一切呈現給程式設計師——這些都是還沒在規模上解決的問題。

更大的轉變,與那份歷久不衰的工藝

退一步,看看 PIM 與近資料運算真正代表了什麼。它們是一場更廣泛叛變的一部分——叛變那個把記憶體當成匯流排另一端被動、愚笨的儲倉的觀念。系統的整個記憶體那一側正在變得主動而多樣:堆在封裝上的高頻寬記憶體非揮發性記憶體儲存類記憶體——它們模糊了「快卻健忘的 RAM」與「慢卻永久的儲存」之間那條老界線——而現在運算更遷進了記憶體與儲存本身。乾淨的馮紐曼圖像——一個 CPU、一片平坦的記憶體、一條匯流排——正在碎裂成一片豐富而異質的地景,運算在許多地方發生,每一處都被挑選來坐在它要處理的資料附近。

然而——這是整一級安靜的一課——底下那份工藝並沒有改變。無論這個時代的技術是雙極性電晶體還是堆疊 DRAM,架構師的工作都一樣:搞懂時間與能量實際花在哪裡、讓常見情況變快、利用區域性、尊重 Amdahl 定律,並把每一分收穫拿去秤它在複雜度、功率,以及它強加的軟體負擔上的代價。PIM 不是逃離記憶體牆的魔法;它只是對計算機架構裡最古老那個問題的又一個有紀律、滿是取捨的答案——我們怎麼讓對的資料與對的運算,在對的時間到達同一個地方?技術會一直變。那個問題,以及它所要求的審慎判斷,會比它們每一個都活得更久。