高效能與平行計算

共享記憶體與分散式記憶體(shared vs distributed memory)

想像一個團隊在寫一份大報告。在一間辦公室裡他們共用一塊白板:任何人都能即時讀或改任何內容,但他們必須協調,免得兩人同時塗改同一處。另一種安排是寫手各坐在不同建築裡,各有自己私人的便箋本,只靠互寄字條合作。這是平行計算的兩種根本模型——共享記憶體與分散式記憶體——而幾乎每個平行程式都建在其一、其二、或兩者的混合之上。

在共享記憶體中,數條執行流(執行緒)全看見同一個位址空間——同一片 RAM 中的同一批陣列。通訊是隱式的:一條執行緒寫入一個值,另一條直接讀它。這是多核 CPU 與 OpenMP 的世界,你拿一個既有迴圈、加上註解,把它的迭代拆給各執行緒。它很方便,但只能擴展到一台機器的核心與共享 RAM,且帶來危險:兩條執行緒碰同一位置時需要同步,否則就出現競爭條件。在分散式記憶體中,每個行程有自己私有的記憶體、看不到別人的;要共享資料就必須明確地透過網路收發訊息。這是計算叢集與 MPI 的世界。它較難寫——你必須切分資料、編排每一次交換——但能擴展到跨多台機器的數千、數百萬核心,因為你只要加更多節點。

這項區別主宰整個平行演算法的形狀。共享記憶體讓細粒度、不規則的平行變得容易,卻在一個節點處撞到天花板;分散式記憶體迫使你思考哪份資料住在哪裡、並為每個跨網路的位元組付明確代價,卻是抵達最大規模的唯一途徑。真實的超級電腦程式通常是混合的:節點間用 MPI 橫跨叢集,每個節點內用 OpenMP 或 GPU 執行緒榨乾其核心。知道你身在哪個模型,就知道你的瓶頸會是什麼——共享世界裡是同步與記憶體頻寬,分散世界裡是通訊延遲與負載平衡。

要在一個 64 核節點上對十億元素陣列求和,共享記憶體勝出:把陣列分給 64 條執行緒,各自加自己那片,再一次最終歸約把它們合起來——沒有資料被複製。要跨一個 1000 節點的叢集求和,每個節點在私有記憶體中擁有自己那塊、在本地加總,再由 MPI 訊息把部分和合起來——唯一跨網路的資料是 1000 個數,而非那十億個。

共享 = 單一位址空間、隱式共享;分散式 = 私有記憶體、明確訊息。

共享記憶體較好寫,但無法神奇地擴展過一台機器,且它的便利藏著競爭條件的陷阱。分散式記憶體能擴展,但每個共享的值都要付一則帶延遲的明確訊息——所以一個逼出大量通訊的糟糕資料切分,可能跑得比序列程式還慢。

又稱
shared-memory vs message-passingthreads vs processes共享記憶體與訊息傳遞執行緒與行程