密集模型的分散式平行化與網路:部署規劃實作
- 作者
- Huang Tzu LinFounder
- 發布日期
- 閱讀時間
- 約 17 分鐘
完成本篇後,你應該能夠
- 區分資料、張量、管線與上下文平行化;
- 解釋 all-reduce、all-gather、reduce-scatter 與 all-to-all,而不把它們視為同一件事;
- 在明確的每 GPU 記憶體預留量下,計算模型是否放得下;
- 估算考量拓撲的通訊下限;
- 選擇留在最快失效域內的張量平行群組;以及
- 產出一份具有可量測驗收條件的部署規劃成品。
完成檢核你能逐項滿足下列所有完成標準嗎?
完整完成標準8
- 每個候選配置都滿足 TP × PP × DP = 8,或明確標記未使用的 GPU;
- 權重記憶體與預留量使用相同 GiB 單位;
- 選定配置中的 rank 都不超過 80 GiB;
- 除非模型無法以其他方式放下,選定 TP 群組不得跨節點;
- 選定方案的集體通訊下限低於 5 ms,或明確回報沒有候選方案通過;
- 部署圖標出獨立副本的失效域;
- 在正式核准前,假設必須包含模型 revision、精度、引擎/runtime 版本、輸入 context 分布、GPU 拓撲,以及實測有效頻寬;以及
- 在承諾容量之前,必須用實際負載測試取代規劃下限。
推論服務之所以要分散到多個加速器,通常有兩個原因:模型放不進單一加速器,或單一加速器達不到延遲與吞吐量目標。兩個理由看似相近,設計結果卻不同。把同一筆請求拆到更多 GPU 上,可以讓模型放得下,卻也會在每次前向傳播加入通訊;複製模型則能提高整體請求吞吐量,但不會降低單一副本所需的記憶體。
這篇設計成在重整後的 I-05 混合專家部署教學之前閱讀。教學任務刻意收斂在一件事:把一個密集型、僅解碼器模型放到多 GPU、多節點叢集上,估算記憶體與集體通訊下限,並淘汰一個雖然放得下、但在操作上很差的配置。
開始之前:先備知識與學習成果
開始之前,你應該已經從 I-00 到 I-02 理解請求生命週期、prefill(提示預處理)與 decode、KV 快取成長,以及連續批次處理。你不需要先有分散式系統或 CUDA 經驗。
完成後,你將能夠:
區分資料、張量、管線與上下文平行化;
解釋 all-reduce、all-gather、reduce-scatter 與 all-to-all,而不把它們視為同一件事;
在明確的每 GPU 記憶體預留量下,計算模型是否放得下;
估算考量拓撲的通訊下限;
選擇留在最快失效域內的張量平行群組;以及
產出一份具有可量測驗收條件的部署規劃成品。
你將建置的成品
你會建立一份 parallelism-plan.json,其中包含:
模型、引擎/runtime revision、精度、層數、隱藏維度與工作負載假設;
叢集拓撲與有效連線假設;
候選
TP × PP × DP配置;每個 rank 的權重記憶體;
decode 集體通訊下限估計;以及
選定的部署方式與失效域理由。
這份成品是規劃,不是基準測試。估算只用來決定哪些配置值得實測;正式容量仍必須由量測決定。
凍結架構與工作負載
本篇所有計算都採用下列假設性、但規格完整的設定。若模型或叢集不同,請先改掉輸入,不要直接套用結果。
輸入 — 假設
--- — ---:
模型 — 密集型 decoder-only Transformer,700 億參數
模型 revision — dense-70b-fixture-r1
服務引擎版本 — tutorial-engine-fixture-1.0
加速器/集體通訊 runtime — tutorial-runtime-fixture-1.0
權重精度 — BF16,每參數 2 bytes
Transformer 區塊 — 80
隱藏維度 — 8,192
輸入 context 分布 — 100% 的請求都是 2,048 input tokens
Decode 批次 — 32 條活躍序列,每條一個 token
叢集 — 2 個節點 × 4 張 GPU = 8 張 GPU
GPU 記憶體 — 每張 80 GiB
Runtime + KV 預留 — 每張 12 GiB
有效節點內頻寬 — 100 GB/s
有效節點間頻寬 — 25 GB/s
節點內步驟延遲 — 5 microseconds
節點間步驟延遲 — 20 microseconds
服務目標 — 最大化線上請求容量,並將 decode 集體通訊下限控制在 5 ms 以下
三個不可變的 fixture 識別碼是教學標籤,不是產品版本。把方案套用到其他環境前,必須連同工作負載與拓撲一起替換。頻寬數字是規劃假設,不是產品規格。你必須用實際部署的通訊函式庫與硬體,量測真實訊息大小與拓撲。目前 vLLM 指南也建議:若模型必須跨節點,應盡量把張量平行限制在節點內,再用管線平行跨節點,因為互連會改變最佳策略(vLLM Parallelism and Scaling)。
四種平行化,四種不同任務
資料平行化
資料平行化建立彼此獨立的模型副本,並把不同請求送到不同副本。一般推論中的資料平行副本不需要同步梯度,因為沒有訓練步驟。資料平行化能提高整體請求容量並隔離故障,但每個副本仍必須容納完整模型;副本內部可以再使用張量或管線平行化。
如果一個副本使用 TP=2,叢集共有 8 張 GPU,那麼 DP=4 代表四個雙 GPU 副本。一個 TP rank 故障會讓所屬雙 GPU 副本失效,但路由器移除故障副本後,其餘三個副本仍可繼續服務。
張量平行化
張量平行化切分單一層內的矩陣。各 rank 協同處理同一批 token,並在每個 transformer 區塊中的多個固定位置交換部分 activation。Megatron-LM 建立了具影響力的層內張量平行設計;目前 vLLM 也明確指出其張量平行實作包含 Megatron-LM 演算法(Shoeybi et al., 2019;vLLM Parallelism and Scaling)。
張量平行化會降低每個 rank 的權重記憶體,卻也會把集體通訊放進延遲關鍵路徑。不能只因為還有 GPU 可用,就持續增加 TP。
管線平行化
管線平行化把連續的層分配給不同 stage。請求的 activation 從 stage 0 傳到 stage 1,而不是讓每個 rank 在每一層內協同。多個 microbatch 可以在不同 stage 上重疊,但管線啟動或排空時,空閒 stage 會形成 pipeline bubble。在 stage 時間相等、m 個 microbatch、p 個 stage 的簡化假設下,理想化管線利用率為:
pipeline_utilization = m / (m + p - 1)
bubble_fraction = (p - 1) / (m + p - 1)若 p=2、m=8,利用率為 8/9 = 88.9%,bubble 比例為 1/9 = 11.1%。真實推論還包含層不平衡、排隊、activation 傳輸與 decode 相依性。GPipe 提供 microbatch 管線的基礎;現代推論引擎則實作各自的排程(Huang et al., 2019)。
上下文平行化
上下文平行化把長序列切分到多個 rank,使注意力狀態與運算不必全部放在單一裝置。以 Ring Attention 為例,它會分散序列區塊並循環傳遞 key-value 區塊,同時把通訊與分塊注意力運算重疊(Liu, Zaharia, and Abbeel, 2023)。
上下文平行化解的是長序列記憶體或注意力運算問題,不是一般短上下文請求的預設吞吐量工具。我們固定為 2,048-token 輸入 context 的分布不需要它,因此選定方案會使用 CP=1。
集體通訊詞彙表
下表描述的是操作完成後資料位於何處,不代表只能採用 ring、tree 或某一種 fused 實作。NCCL 的現行文件直接定義了這些操作的語意(NVIDIA NCCL collective API)。
集體操作 — 操作完成後的結果 — 常見推論用途
All-reduce — 對對應值做歸約,並在每個 rank 留下完整歸約結果 — 合併張量平行的部分總和
Reduce-scatter — 對對應值做歸約,並在每個 rank 只留下其中一個結果分片 — 保持歸約後的 activation 為分片狀態
All-gather — 串接各分片,並在每個 rank 留下完整結果 — 重建每個 rank 都需要的 activation
All-to-all — 每個 rank 都向其他 rank 發送不同分片 — 許多專家平行配置中的 token 分派與回傳
概念上,all-reduce 可以拆成 reduce-scatter 再接 all-gather,但最佳化函式庫可能採用其他演算法。同樣地,專家平行系統也不會永遠使用同一種 collective;I-05 會說明引擎、DP/TP 配置、訊息大小與硬體如何共同決定實作。
兩個可重現的規劃公式
公式一:每個 Rank 的權重記憶體
對權重平均分片的密集模型:
weight_bytes_total = parameters × bytes_per_parameter
weight_gib_total = weight_bytes_total / 2^30
weight_gib_per_rank = weight_gib_total / TP / PP
usable_gib_per_gpu = gpu_gib - runtime_and_kv_reserve_gib
fits = weight_gib_per_rank <= usable_gib_per_gpu這只是第一層篩選。層數不均、embedding、輸出 head、allocator 行為、CUDA graph、activation 與量化 metadata,都可能讓看似剛好放得下的配置失敗。因此,12 GiB 預留量必須明寫,而不能藏在假設裡。
公式二:Ring All-Reduce 規劃下限
對包含 p 個 rank、每個 rank 邏輯訊息大小為 M bytes 的 ring all-reduce,一個實用的規劃近似式是:
wire_bytes_per_rank ≈ 2 × (p - 1) / p × M
time_per_collective ≈ wire_bytes_per_rank / effective_bandwidth
+ 2 × (p - 1) × link_step_latency這不是效能預測。它忽略 protocol 選擇、chunking、資源競爭、拓撲感知 tree、重疊、kernel launch 成本與壅塞。它只用來提前暴露「通訊下限已經不可接受」的配置。
完整範例:部署 70B 級模型
步驟一:計算權重記憶體
total weight bytes = 70,000,000,000 × 2
= 140,000,000,000 bytes
total weight GiB = 140,000,000,000 / 1,073,741,824
= 130.39 GiB
usable per GPU = 80 - 12
= 68 GiB候選記憶體結果:
配置 — 每 GPU 權重 GiB — 符合 68 GiB 預算? — 獨立副本數
--- — ---: — ---: — ---:
TP1 × PP1 × DP8 — 130.39 — 否 — 8
TP2 × PP1 × DP4 — 65.19 — 是 — 4
TP4 × PP1 × DP2 — 32.60 — 是 — 2
TP8 × PP1 × DP1 — 16.30 — 是 — 1
TP4 × PP2 × DP1 — 16.30 — 是 — 1
通過明確記憶體預留條件的最小張量平行群組是 TP=2。選擇 TP4 或 TP8,會讓每筆請求使用超過「放得下」所需的 GPU,並減少獨立副本數。
對 TP4 × PP2 候選配置,後面的計算器只包含 TP collective 流量,不包含 PP stage 之間的 activation transfer 或 bubble 成本。在量測這些額外成本前,不能選用該列。
步驟二:計算 Decode Activation 訊息
每條活躍 decode 序列有一個 BF16 hidden activation:
M = decode_batch × hidden_size × bytes_per_element
= 32 × 8,192 × 2
= 524,288 bytes
= 0.5 MiB本練習假設每個 transformer 區塊有兩次 all-reduce,因此一次 decode 前向傳播共有 2 × 80 = 160 次 collective。實際 collective 位置由模型和引擎決定;在把結果用於操作決策前,必須以 trace 驗證。
步驟三:比較節點內 TP2 與跨節點 TP8
對節點內連線上的 TP=2:
wire bytes/rank = 2 × 1/2 × 524,288 = 524,288 bytes
transfer time = 524,288 / 100,000,000,000 = 5.24 microseconds
latency term = 2 × 1 × 5 = 10 microseconds
one collective ≈ 15.24 microseconds
160 collectives ≈ 2.44 milliseconds對跨越兩節點的 TP=8:
wire bytes/rank = 2 × 7/8 × 524,288 = 917,504 bytes
transfer time = 917,504 / 25,000,000,000 = 36.70 microseconds
latency term = 2 × 7 × 20 = 280 microseconds
one collective ≈ 316.70 microseconds
160 collectives ≈ 50.67 milliseconds兩種配置都放得下模型,但只有 TP2 符合練習的 <5 ms 集體通訊下限條件,而且 TP2 還能建立四個獨立副本。因此選定配置為:
Node A: [GPU0, GPU1] replica 0, TP2 [GPU2, GPU3] replica 1, TP2
Node B: [GPU4, GPU5] replica 2, TP2 [GPU6, GPU7] replica 3, TP2
PP=1, DP=4, CP=1這是考量拓撲的部署決策,不是在宣稱 TP2 永遠最佳。更大的 KV 需求、不同精度,或無法放進單一節點的模型,都可能改變答案。
建立成品
將以下內容存為 plan_parallelism.py,執行後重新導向輸出:
import json
import math
PARAMETERS = 70_000_000_000
MODEL_REVISION = "dense-70b-fixture-r1"
ENGINE_VERSION = "tutorial-engine-fixture-1.0"
RUNTIME_VERSION = "tutorial-runtime-fixture-1.0"
BYTES_PER_PARAMETER = 2
GPU_GIB = 80
RESERVE_GIB = 12
LAYERS = 80
HIDDEN = 8192
INPUT_CONTEXT_DISTRIBUTION = {"2048": 1.0}
DECODE_BATCH = 32
COLLECTIVES_PER_LAYER = 2
CANDIDATES = [
{"tp": 1, "pp": 1, "dp": 8, "domain": "single_gpu"},
{"tp": 2, "pp": 1, "dp": 4, "domain": "intra_node"},
{"tp": 4, "pp": 1, "dp": 2, "domain": "intra_node"},
{"tp": 8, "pp": 1, "dp": 1, "domain": "inter_node"},
{"tp": 4, "pp": 2, "dp": 1, "domain": "pipeline_across_nodes"},
]
LINKS = {
"single_gpu": {"bandwidth_gb_s": math.inf, "step_us": 0},
"intra_node": {"bandwidth_gb_s": 100, "step_us": 5},
"inter_node": {"bandwidth_gb_s": 25, "step_us": 20},
"pipeline_across_nodes": {"bandwidth_gb_s": 100, "step_us": 5},
}
total_weight_gib = PARAMETERS * BYTES_PER_PARAMETER / 2**30
message_bytes = DECODE_BATCH * HIDDEN * 2
usable_gib = GPU_GIB - RESERVE_GIB
rows = []
for candidate in CANDIDATES:
tp = candidate["tp"]
pp = candidate["pp"]
weight_gib = total_weight_gib / tp / pp
link = LINKS[candidate["domain"]]
if tp == 1:
floor_ms = 0.0
else:
wire_bytes = 2 * (tp - 1) / tp * message_bytes
transfer_s = wire_bytes / (link["bandwidth_gb_s"] * 1e9)
latency_s = 2 * (tp - 1) * link["step_us"] * 1e-6
floor_ms = (transfer_s + latency_s) * LAYERS * COLLECTIVES_PER_LAYER * 1e3
rows.append({
**candidate,
"weight_gib_per_gpu": round(weight_gib, 2),
"fits": weight_gib <= usable_gib,
"decode_collective_floor_ms": round(floor_ms, 2),
"passes": weight_gib <= usable_gib and floor_ms < 5,
})
artifact = {
"assumptions": {
"parameters": PARAMETERS,
"model_revision": MODEL_REVISION,
"engine_version": ENGINE_VERSION,
"runtime_version": RUNTIME_VERSION,
"bytes_per_parameter": BYTES_PER_PARAMETER,
"gpu_gib": GPU_GIB,
"runtime_and_kv_reserve_gib": RESERVE_GIB,
"layers": LAYERS,
"hidden_size": HIDDEN,
"input_context_distribution": INPUT_CONTEXT_DISTRIBUTION,
"decode_batch": DECODE_BATCH,
},
"candidates": rows,
"selected": {"tp": 2, "pp": 1, "dp": 4, "cp": 1},
}
print(json.dumps(artifact, indent=2))執行:
python plan_parallelism.py > parallelism-plan.json預期檢查結果:
TP1: fits=false
TP2: weight_gib_per_gpu=65.19, decode_collective_floor_ms=2.44, passes=true
TP8: weight_gib_per_gpu=16.30, decode_collective_floor_ms=50.67, passes=false
selected: TP2 × PP1 × DP4 × CP1刻意破壞的配置
下列配置在某些分散式 runtime 中足以啟動,但對本工作負載而言是壞的:
world_size: 8
tensor_parallel_size: 8
pipeline_parallel_size: 1
data_parallel_size: 1
placement: spread_across_two_nodes
reason: "use every GPU for the lowest per-rank weight memory"它因三個可量測原因失敗:
TP2 已符合 68 GiB 容量預算,因此 TP8 沒有解決任何剩餘的放置問題。
規劃下限約為每次 decode 前向傳播 50.67 ms,高於 5 ms 條件。
任一 rank 故障都會移除唯一副本;選定的 TP2/DP4 方案有四個副本失效域。
另一種錯誤可能要到負載升高才浮現:如果部署 rank 時沒有檢查 GPU-to-NIC 與 GPU-to-GPU affinity,流量可能經過較慢路徑。目前 NCCL 會感知拓撲,但部署仍需暴露正確的裝置和網路介面(NVIDIA NCCL overview)。
第二個損壞的 All-to-All 方案
假設一個長上下文 backend 在 CP=8 上,每個區塊使用兩次 sequence-transpose all-to-all,而且每個 rank 持有 4,096 個 BF16 token activation,hidden size 為 8,192。Unified Sequence Parallelism 說明了 all-to-all 與 ring-based sequence parallelism 為何有不同拓撲取捨(Fang and Zhao, 2024)。在本練習的簡化假設下:
local activation M = 4,096 × 8,192 × 2
= 67,108,864 bytes = 64 MiB
bytes sent to other ranks = 7/8 × M
= 58,720,256 bytes
one bandwidth-only floor = 58,720,256 / 25,000,000,000
= 2.35 ms
160 all-to-alls = 2.35 × 160
= 375.81 ms即使還沒加入 per-peer latency、contention 或 synchronization,這個方案已經失敗。實作可能重疊大部分流量或選擇其他演算法,但那是必須量測的可能性,不是把資料移動從規劃中刪掉的許可。
動手實作
針對以下新需求修改成品產生器:
輸出密集型工作負載;
每張 GPU 為 KV 快取與 runtime 預留 24 GiB;
仍使用相同 8 張 GPU 與連線假設;
集體通訊下限仍為 5 ms。
在 placement-decision.md 回答:
TP2 是否仍放得下?
最小可放下的 TP 大小是多少?
還能保有幾個獨立副本?
最小可放下配置是否跨越節點?
若答案改變,改變它的是模型容量、集體通訊下限,還是故障隔離?
預期記憶體計算:
usable per GPU = 80 - 24 = 56 GiB
TP2 weight = 65.19 GiB -> does not fit
TP4 weight = 32.60 GiB -> fitsTP4 × DP2 是最小可放下的配置,而且每個 TP 群組都留在單一四 GPU 節點內。相同公式算出的集體通訊下限約為 6.06 ms,因此它不符合原始 5 ms 目標。這是刻意設計的結果:它示範一組條件可能沒有任何合格配置。正確做法是調整硬體、精度、預留量或 SLO,再重新測試;而不是隱藏失敗條件。
可量測的驗收標準
你的教學成品只有在以下條件全部成立時才算通過:
每個候選配置都滿足
TP × PP × DP = 8,或明確標記未使用的 GPU;權重記憶體與預留量使用相同 GiB 單位;
選定配置中的 rank 都不超過 80 GiB;
除非模型無法以其他方式放下,選定 TP 群組不得跨節點;
選定方案的集體通訊下限低於 5 ms,或明確回報沒有候選方案通過;
部署圖標出獨立副本的失效域;
在正式核准前,假設必須包含模型 revision、精度、引擎/runtime 版本、輸入 context 分布、GPU 拓撲,以及實測有效頻寬;以及
在承諾容量之前,必須用實際負載測試取代規劃下限。
提取練習
哪種平行化策略會建立獨立的請求服務副本?
為什麼張量平行化可能讓模型放得下,卻惡化 decode 延遲?
All-gather 和 reduce-scatter 在語意上有何差異?
什麼情況適合使用上下文平行化?
在計算範例中,為什麼 TP2 優於 TP8?
如果沒有候選配置同時符合記憶體與通訊條件,該怎麼辦?
解答
資料平行化。
它會切分權重,但也在前向傳播關鍵路徑加入反覆執行的集體通訊。
All-gather 在每個 rank 上從分片重建完整張量;reduce-scatter 則歸約數值,並在每個 rank 上只留下其中一個結果分片。
當序列狀態記憶體或長上下文注意力運算無法在單一 rank 放下或達成目標時;它不是短上下文吞吐量的預設工具。
TP2 是最小可放下的群組、保留在節點內、建立四個副本,且規劃下限為 2.44 ms;跨節點 TP8 則約為 50.67 ms。
回報條件組合不可行,接著修改精度、硬體、預留量、拓撲或目標並重新量測。不要悄悄刪除條件。
本文不涵蓋的內容
本篇涵蓋密集模型部署,不處理專家路由、專家負載平衡或專家平行 collective;這些會在 I-05 重組後接續。本篇也不主張簡化 ring 公式可以預測正式延遲。Kernel trace、collective benchmark、排隊、KV 容量與端對端負載測試仍然不可少。
下一篇 P0 教學 I-07 會把已部署配置轉成可重現基準測試,推導符合 SLO 的容量,並定義能及時發現規劃錯誤的遙測資料。
來源與延伸閱讀
Shoeybi, M., Patwary, M., Puri, R., et al. “Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism.” 2019。具影響力的層內張量平行設計第一手來源。arxiv.org/abs/1909.08053
Narayanan, D., Shoeybi, M., Casper, J., et al. “Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM.” 2021。組合張量、管線與資料平行化的第一手來源。arxiv.org/abs/2104.04473
Huang, Y., Cheng, Y., Bapna, A., et al. “GPipe: Efficient Training of Giant Neural Networks Using Pipeline Parallelism.” 2019。Microbatch 管線平行與 bubble 攤提的第一手來源。arxiv.org/abs/1811.06965
Liu, H., Zaharia, M., and Abbeel, P. “Ring Attention with Blockwise Transformers for Near-Infinite Context.” 2023。將長序列注意力分散到多個裝置的第一手來源。arxiv.org/abs/2310.01889
Fang, J., and Zhao, S. “USP: A Unified Sequence Parallelism Approach for Long Context Generative AI.” 2024。比較 all-to-all 與 ring-based sequence-parallel 方法的第一手來源。arxiv.org/abs/2405.07719
NVIDIA. “Collective Communication Functions.” NCCL 現行文件,存取日期 2026-08-29。All-reduce、all-gather、reduce-scatter 與 all-to-all 語意的第一方定義。github.com/NVIDIA/nccl
vLLM Project. “Parallelism and Scaling.” 現行文件,存取日期 2026-08-29。單節點與多節點系統中張量及管線平行的第一方部署指南。docs.vllm.ai
閱讀順序
推論系統
章節 06 / 08
Huang Tzu Lin
With over eight years in autonomous robotics, there's a strong passion for incorporating cutting-edge technologies and innovative approaches. Dedicated to transforming the latest research and insights into practical applications, this journey pushes the limits of possibility.
