為什麼每個大模型都是一支分散式程式
沒有單一加速器裝得下前沿模型的權重、梯度與優化器狀態,所以分散式訓練會把工作切片到許多張卡上。但切片是容易的部分;難的是這些切片必須不斷地達成一致。資料平行的複本必須每步平均它們的梯度;張量平行的切片必須在一層之內交換部分激發值;FSDP 必須即時收集每層的參數、用完後再重新散開。所有這些一致性,都用一小撮集體通訊(collective communication)運算來表達。
集體通訊的詞彙表
NCCL(NVIDIA Collective Communications Library,NVIDIA 集體通訊函式庫)是 NVIDIA 硬體上事實上的實作,它提供少數幾個基本運算,每種平行策略都從它們建構而成。all-reduce(全歸約)把一個張量在所有 rank 上求和、再把結果發給每個人——這就是梯度平均。all-gather(全收集)把每個 rank 的切片彙集成完整張量、發給每個人——這重建了 FSDP 的參數。reduce-scatter(歸約散開)求和後讓每個 rank 各持一片——這產生切片後的梯度。broadcast(廣播)與 all-to-all(全互換)補齊整套;最後一個是混合專家(mixture-of-experts)路由的主力。
環狀 all-reduce:設計上即頻寬最優
一個樸素的 all-reduce 把每份梯度都送到一個主節點,會讓那條連結成為瓶頸、擴展性極差。環狀 all-reduce(ring all-reduce)改把各 rank 排成一個邏輯環、讓資料塊沿環傳遞:一個 reduce-scatter 階段把資料塊繞環循環並求和,接著一個 all-gather 階段把算完的資料塊繞環送回。妙處在於每個 rank 所送的資料量變得與 rank 數無關——大約是張量大小的 2 倍,不論你有 8 張 GPU 還是 8000 張。這種頻寬最優性,正是環狀(及其樹狀與階層式的表親)撐起大規模訓練的原因。
環形 all-reduce 每個 rank 傳輸 2(N−1)/N × |g| 位元組,隨 N 增大趨於與 N 無關的常數 2|g|/B——這正是它頻寬最優的含義。
網路布料:NVLink、InfiniBand 與拓撲
集體運算的速度,受限於底下的線路,而那些線路並不均勻。NVLink 以每秒數 TB——接近 HBM 的速度——連接節點內部的 GPU;而節點之間則由 InfiniBand 或乙太網路橫跨資料中心相連,速度也許低一個數量級。這個階層是關於一座叢集最重要的一件事。NCCL 具備拓撲感知(topology-aware):它探勘網路布料、建立讓最重流量留在最快連結上的環與樹。你的工作是放置平行策略去匹配它:把最聒噪的維度(張量平行)放進節點內、走 NVLink,把最能容忍延遲的維度(資料/管線平行)放到較慢的節點間網路布料上。
把通訊藏在運算後面
即使是頻寬最優的集體運算也要花時間,而支付這筆時間最乾淨的方式,是不花時間——藉由讓通訊與運算重疊。當 GPU 在算第 L 層的算術時,網路可以在背景 all-gather 第 L+1 層的參數、或 all-reduce 第 L-1 層的梯度。做得好,通訊完全消失在運算之後、叢集近乎線性擴展;做得差,各 rank 在每次集體運算處卡住,你付足全價。達成重疊,是每個正經訓練堆疊的核心工程問題。
完美重疊可將通信完全隱藏:每步耗時是計算與通信的最大值,而非二者之和。
# FSDP-style overlap, schematically
for layer in layers:
prefetch_all_gather(layer.next) # launch comm now, async
y = layer.forward(x) # compute hides the gather
x = y
# backward mirrors it: reduce-scatter grads of layer L while computing L-1