텐서 병렬화¶
목차¶
- 개념
- 현재 런타임 지원 범위
- 가중치 샤딩 전략
- 통신 패턴
- 아키텍처 인식 샤드 플랜
- MoE 전문가 병렬화
- 임베딩 및 LM Head 샤딩
- TP 스케줄러 및 실행기
- 설정
- 파이프라인 병렬화와의 조합 (2D PP × TP)
개념¶
텐서 병렬화(TP)는 개별 가중치 행렬을 GPU별로 수평 분할합니다. 각 GPU는 전체 행렬의 샤드 하나씩을 보유합니다. 모든 GPU는 동일한 입력 토큰을 처리하되, 각각 가중치의 서로 다른 부분을 계산합니다. 각 레이어가 끝나면 통신 집합 연산(all-reduce, all-gather)으로 부분 결과를 완전한 활성화로 결합한 뒤 다음 레이어로 진행합니다.
graph LR
subgraph Input
X[Input: shape B×S×D]
end
subgraph "GPU 0 (Rank 0)"
W0["W_shard_0<br/>(D × D/2)"]
P0["Partial output<br/>(B×S×D/2)"]
end
subgraph "GPU 1 (Rank 1)"
W1["W_shard_1<br/>(D × D/2)"]
P1["Partial output<br/>(B×S×D/2)"]
end
X --> W0
X --> W1
W0 --> P0
W1 --> P1
P0 -->|all-gather| OUT[Full output: B×S×D]
P1 -->|all-gather| OUT 이것은 각 GPU가 서로 다른 레이어를 실행하는 파이프라인 병렬화와는 다릅니다. TP에서는 모든 GPU가 모든 레이어에 참여합니다.
현재 런타임 지원 범위¶
현재 mlxcel에 들어간 TP 런타임은 단일 프로세스 내 멀티 랭크(in-process multi-rank) 방식입니다. 하나의 mlxcel generate 또는 mlxcel serve 프로세스가 여러 랭크 샤드를 로드하고 한 프로세스 안에서 lockstep으로 실행합니다. 이 경로에서는 --node-role, --peers, 분산 클러스터 설정이 필요하지 않습니다.
현재 tp_size > 1에서 지원하는 런타임:
- dense Llama 계열
- dense Qwen2 / Qwen2.5 계열
- dense Qwen3 계열
- dense Qwen3.5 text 계열
- Gemma 3 텍스트 계열
- Gemma 4 텍스트 계열
- ERNIE 4.5 dense 계열
- Hunyuan v1 Dense 계열
현재 제약:
--tp-embedding-mode는replicated여야 합니다--tp-lm-head-mode는replicated여야 합니다- LoRA adapter는 TP와 함께 지원하지 않습니다
- dense Llama, Qwen2/Qwen2.5, Qwen3, Qwen3.5 text, Gemma 3 text, Gemma 4 text, ERNIE 4.5, Hunyuan v1 Dense에서는 TP server batching이 활성화됩니다
- TP 런타임은 배치된 행을 하나의 네이티브 배치 forward로 디코드하며(
BatchedDecodeKind::Native), 서버는 이들에 대해decode path: native batched forward를 로그로 남깁니다 - loaded-model 폴백이 필요한 Gemma 4 체크포인트(KV 공유 레이어가 있는 E2B 계열)는 server에서 sequential 실행으로 강제됩니다
로컬 CUDA에서 검증한 모델:
models/llama-3.2-1b-4bitmodels/qwen2.5-0.5b-4bit(tp=2, greedy 24토큰 parity)models/qwen2.5-7b-4bit(tp=4)models/qwen3-0.6b-4bitmodels/qwen3.5-0.8b-4bit(tp=2)models/qwen3.5-4b-4bit(tp=4)models/qwen3.5-9b-4bit(tp=2,tp=4, stepwise CUDA parity)models/qwen3.5-27b-4bit(tp=4, stepwise CUDA parity)models/gemma3-1b-4bitmodels/ernie-4.5-0.3b-4bitmodels/hunyuan-1.8b-4bit
현재 비활성 상태:
- TP + LoRA
가중치 샤딩 전략¶
Plan vocabulary와 shipping runtime의 차이
Plan type은 의도한 TP design surface를 설명합니다. production runtime은 dense model의 column/row sharding과 replicated embedding/LM head를 활성화합니다. 아래 VocabParallel, MoE expert mode, SSM/Mamba plan은 구현·설계 primitive이며 operator-supported runtime을 뜻하지 않습니다.
src/distributed/tensor_parallel/shard_strategy.rs에 정의되어 있습니다:
Column Parallel¶
가중치 행렬의 **출력 차원(열)**을 랭크에 걸쳐 분할합니다. 각 랭크는 출력 피처의 일부를 계산합니다.
block-beta
columns 3
W["W: [D_in × D_out]"]:3
R0["Rank 0\nW[:, 0 : D_out/N]"] R1["Rank 1\nW[:, D_out/N : 2·D_out/N]"] Rn["...\nRank N-1"] 사용처: 어텐션 Q/K/V 프로젝션, FFN gate 및 up 프로젝션.
Column-parallel 연산 후 완전한 출력이 즉시 필요하면 all-gather를 수행합니다. 다음 연산이 row-parallel이라면 reduce-scatter를 뒤로 미룰 수 있습니다.
Row Parallel¶
가중치 행렬의 **입력 차원(행)**을 랭크에 걸쳐 분할합니다. 각 랭크는 부분 합에 기여하는 부분 활성화를 보유하고 이를 all-reduce로 합산합니다.
block-beta
columns 3
W["W: [D_in × D_out]"]:3
R0["Rank 0\nW[0 : D_in/N, :]"] R1["Rank 1\nW[D_in/N : 2·D_in/N, :]"] Rn["...\nRank N-1"] 사용처: 어텐션 출력 프로젝션, FFN down 프로젝션.
Row-parallel 행렬곱 후에는 all-reduce(합산)를 적용해 올바른 결합 출력을 만듭니다.
Vocab Parallel¶
임베딩 테이블과 LM head의 어휘 차원을 랭크에 걸쳐 분할합니다. 각 랭크는 토큰 어휘의 연속적인 슬라이스를 담당합니다.
사용처: 임베딩 조회, LM head 출력 로짓.
Replicated¶
가중치가 모든 랭크에 완전히 복제됩니다. 샤딩이나 통신이 필요하지 않습니다. 레이어 정규화 파라미터 및 바이어스 벡터와 같은 소형 텐서에 사용됩니다.
통신 패턴¶
src/distributed/tensor_parallel/collective.rs에 정의되어 있습니다:
graph TD
subgraph AllReduce["All-Reduce (Ring)"]
AR0[Rank 0<br/>partial sum] -->|Reduce-scatter| RS[Each rank<br/>holds one reduced chunk]
AR1[Rank 1<br/>partial sum] --> RS
AR2[Rank 2<br/>partial sum] --> RS
RS -->|All-gather| AG[All ranks<br/>hold full reduced tensor]
end
subgraph AllGather["All-Gather"]
AG0[Rank 0 shard] -->|ring forwarding| FG[All ranks<br/>hold full tensor]
AG1[Rank 1 shard] --> FG
end
subgraph ReduceScatter["Reduce-Scatter"]
RS0[Rank 0 full tensor] -->|accumulate + scatter| RSO[Each rank<br/>holds 1/N reduced shard]
RS1[Rank 1 full tensor] --> RSO
end All-reduce (ring):
TP에서 가장 일반적인 집합 연산입니다. Row-parallel 행렬곱 후 모든 랭크에 걸쳐 부분 결과를 합산하는 데 사용됩니다. 두 단계의 링 알고리즘을 사용합니다: reduce-scatter에 이어 all-gather. 랭크당 총 데이터 전송량: 2 × (N-1)/N × tensor_size.
All-gather:
샤딩된 출력을 완전한 텐서로 조합하는 데 사용됩니다. 각 랭크가 자신의 샤드를 기여하며 모든 랭크가 최종적으로 전체 텐서를 보유합니다.
Reduce-scatter:
Row-parallel 연산의 결과가 곧바로 column-parallel 연산으로 이어지는 최적화된 파이프라인에서 사용됩니다. 출력의 샤드만 필요한 경우 all-gather 단계를 피할 수 있습니다.
모든 집합 연산은 링 토폴로지를 사용합니다. 랭크 r은 각 단계에서 (r+1) % N으로 전송하고 (r+N-1) % N으로부터 수신합니다. N-1 단계에 걸쳐 모든 랭크는 다른 모든 랭크와 정확히 한 번씩 통신합니다.
아키텍처 인식 샤드 플랜¶
mlxcel은 src/distributed/tensor_parallel/plan_generator.rs를 통해 아키텍처별 샤드 플랜을 생성합니다. 플랜은 어느 가중치 텐서에 어떤 ShardStrategy를 적용하고 어떤 CommPattern이 이어지는지를 인코딩합니다.
표준 트랜스포머 (Llama, Qwen, Gemma 등)¶
| 레이어 | 가중치 | 샤드 전략 | 통신 패턴 |
|---|---|---|---|
| Attention | q_proj.weight | ColumnParallel | AllGather (output dim = nheads × headdim) |
k_proj.weight | ColumnParallel | GQA 인식: 소형 KV 헤드 복제 가능 | |
v_proj.weight | ColumnParallel | GQA 인식 | |
o_proj.weight | RowParallel | AllReduce | |
| FFN | gate_proj.weight | ColumnParallel | - |
up_proj.weight | ColumnParallel | - | |
down_proj.weight | RowParallel | AllReduce |
GQA 인식 어텐션 샤딩¶
Grouped Query Attention(GQA)을 사용하는 모델은 KV 헤드 수가 Q 헤드 수보다 훨씬 적은 경우가 많습니다. 샤드 플랜은 이를 고려합니다:
num_kv_heads >= tp_size인 경우: K/V 프로젝션이 일반적으로 샤딩됩니다.num_kv_heads < tp_size인 경우: K/V 프로젝션을 모든 랭크에 복제해 KV 헤드가 랭크당 하나 미만으로 단편화되는 일을 막습니다.
이는 src/distributed/tensor_parallel/parallel_attention.rs에서 계산됩니다.
graph LR
subgraph "GQA Sharding (tp_size=4, 32 Q heads, 8 KV heads)"
Q["Q heads: 32<br/>2 per rank"]
K["K heads: 8<br/>2 per rank"]
V["V heads: 8<br/>2 per rank"]
end
subgraph "GQA Replication (tp_size=8, 4 KV heads)"
Q2["Q heads: 32<br/>4 per rank"]
K2["K heads: 4<br/>replicated (< 1 per rank)"]
V2["V heads: 4<br/>replicated (< 1 per rank)"]
end SSM/Mamba¶
Mamba 및 Mamba2 모델의 경우, 합성곱 및 SSM 상태 행렬이 채널 차원을 따라 샤딩되며 동일한 column/row parallel 패턴을 따릅니다.
MoE 전문가 병렬화¶
아래 mode는 shard-plan design을 설명합니다. 현재 production tp_size > 1 runtime은 위에 나열한 dense family로 제한되므로 이 mode들은 활성화되지 않습니다.
Mixture-of-Experts 모델(Mixtral, DeepSeek V2/V3, Qwen MoE 등)의 경우, mlxcel은 --tp-moe-mode로 설정 가능한 두 가지 MoE 샤딩 모드를 지원합니다:
Expert Parallel (기본값)¶
전체 전문가가 라운드 로빈으로 랭크에 할당됩니다: 전문가 i는 전적으로 랭크 i % tp_size에 위치합니다. 각 랭크의 전문가 가중치는 모두 로컬에 있으므로 전문가 내부 통신이 필요하지 않습니다.
flowchart LR
subgraph "Rank 0"
E0["Expert 0"]
E2["Expert 2"]
end
subgraph "Rank 1"
E1["Expert 1"]
E3["Expert 3"]
end
Router --> E0 & E1 & E2 & E3 num_experts >= tp_size일 때 가장 효과적입니다. 라우터는 여전히 모든 랭크에서 실행되지만 각 랭크는 로컬에 할당된 전문가만 실행합니다.
Within-Expert Parallel¶
각 전문가의 내부 FFN 가중치는 일반 조밀 FFN과 동일한 column-parallel gate/up, row-parallel down 프로젝션 패턴으로 모든 랭크에 걸쳐 샤딩됩니다.
num_experts < tp_size일 때 가장 효과적입니다(전문가 수가 적고 GPU가 많은 드문 경우).
src/distributed/tensor_parallel/parallel_moe.rs에 정의되어 있습니다.
임베딩 및 LM Head 샤딩¶
Configuration type은 이 strategy를 모델링하지만 런타임은 embedding과 LM-head mode 모두 replicated를 요구합니다. 아래 sharded 대안은 production에서 활성화되지 않습니다.
--tp-embedding-mode 및 --tp-lm-head-mode로 설정 가능한 두 가지 모드:
| 모드 | 설명 |
|---|---|
replicated (기본값) | 임베딩 테이블과 LM head가 모든 랭크에 완전히 복제됩니다. 더 단순하며 일반적인 어휘 크기에 적합합니다. |
vocab_parallel | 어휘 차원이 랭크에 걸쳐 샤딩됩니다. 메모리를 절약하며 임베딩 조회와 LM head 로짓에 all-gather가 필요합니다. |
어휘가 매우 큰(10만 토큰 초과) 모델에서 메모리가 주된 제약이라면 vocab-parallel 모드를 권장합니다.
TP 스케줄러 및 실행기¶
TP 실행은 두 가지 컴포넌트로 조율됩니다:
TP 스케줄러 (랭크 0 전용)¶
src/distributed/tensor_parallel/tp_scheduler.rs
스케줄러는 랭크 0에서만 실행됩니다. 스케줄러의 역할:
- 새 시퀀스를 수용하고 프리필 큐를 관리합니다.
- 배치를 구성합니다 (각 단계에서 프리필 또는 디코드할 시퀀스를 선택).
- 각 추론 단계 전에 다른 모든 랭크에
StepDecision메시지를 브로드캐스트합니다. - 토큰 샘플링을 조율합니다: 랭크 0이 다음 토큰을 샘플링하고 결과를 브로드캐스트합니다.
- 연속 배치: GPU 활용률을 극대화하기 위해 프리필과 디코드 단계를 교차합니다.
다른 랭크는 독립적인 스케줄링 결정을 내리지 않습니다. 모든 랭크는 각 단계를 실행하기 전에 스케줄러의 브로드캐스트를 기다립니다.
시퀀스 생명주기:
stateDiagram-v2
[*] --> Waiting: request submitted
Waiting --> PendingPrefill: admitted to batch
PendingPrefill --> Decoding: prefill complete
Decoding --> Completed: EOS or max_tokens
Decoding --> Evicted: preempted
Evicted --> Waiting: re-admitted
Completed --> [*] TP 실행기 (전체 랭크)¶
src/distributed/tensor_parallel/tp_executor.rs
모든 랭크(랭크 0 포함)는 TPExecutor를 실행합니다. 실행기의 역할:
- 랭크 0 브로드캐스트에서
StepDecision값을 수신합니다. - 모든 랭크에서 동일한 시퀀스 상태를 유지합니다 (잠금 단계 실행을 위해).
- 각 단계 전에 배리어 동기화에 참여합니다.
- 디코드 단계에서 랭크 0이 다음 토큰을 샘플링하고 다른 랭크는 브로드캐스트로 샘플링된 토큰을 받습니다.
- 랭크 헬스 상태를 보고하며 오류가 발생하면 전파해 전체 TP 그룹을 중지합니다.
sequenceDiagram
participant S as Scheduler (rank 0)
participant E0 as Executor rank 0
participant E1 as Executor rank 1
participant E2 as Executor rank 2
S->>E0: broadcast StepDecision
S->>E1: broadcast StepDecision
S->>E2: broadcast StepDecision
Note over E0,E2: All ranks execute forward pass in lockstep
E0->>E1: all-reduce (per layer)
E1->>E2: all-reduce (per layer)
E2->>E0: all-reduce (per layer)
Note over E0: Rank 0 samples next token
E0->>E1: broadcast sampled token
E0->>E2: broadcast sampled token
Note over S: Update sequence state 설정¶
| CLI 플래그 | 설명 | 기본값 |
|---|---|---|
--tp-size N | 텐서 병렬 랭크 수. 2의 거듭제곱이어야 합니다. | 1 (비활성화) |
--tp-moe-mode | MoE 샤딩 모드: expert_parallel 또는 within_expert | expert_parallel |
--tp-embedding-mode | 임베딩 샤딩: replicated 또는 vocab_parallel | replicated |
--tp-lm-head-mode | LM head 샤딩: replicated 또는 vocab_parallel | replicated |
현재 런타임에서는 임베딩 관련 두 플래그를 모두 replicated로 두어야 합니다.
예시: 한 머신에서 2랭크 TP 텍스트 생성:
mlxcel generate \
--model ./models/llama-3.2-1b-4bit \
--prompt "텐서 병렬화를 한 문장으로 설명해 주세요." \
--tp-size 2
예시: 한 머신에서 2랭크 TP 서버:
파이프라인 병렬화와의 조합 (2D PP × TP)¶
텐서 병렬화는 파이프라인 병렬화와 조합해 2D (pp_stage, tp_rank) 메시를 구성할 수 있습니다. PP-only 토폴로지에서 한 스테이지가 여전히 한 노드의 메모리를 초과할 때 사용합니다. 대표 사례는 Llama-70B 급 모델을 중형 GPU 4대 위에 2 PP 스테이지 × 2 TP 랭크 메시로 서빙하는 경우입니다.
참고: 동일한 메시를 PP 관점에서 서술한 내용은 파이프라인 병렬화 → 텐서 병렬화와의 조합 (2D PP × TP) 에서 확인할 수 있습니다.
스테이지 내부의 TP 동작¶
한 파이프라인 스테이지 내부의 TP 컬렉티브 레이어는 위의 1D 설명과 동일합니다. 랭크 0이 admission과 step 브로드캐스트를 주도하고 해당 스테이지의 모든 랭크가 그 스테이지 레이어에 대한 링 토폴로지 all-reduce / reduce-scatter / all-gather에 참여합니다. TP 피어 검색은 스테이지 단위로 한정됩니다: all_reduce_sum, all_gather, reduce_scatter는 같은 pp_stage를 공유하는 TP 랭크들하고만 통신합니다.
스테이지 경계의 PP 동작¶
PP 스테이지 경계에서, 각 tp_rank는 자기 활성화 샤드를 다음 스테이지의 같은 tp_rank를 소유하는 노드에 전달합니다. 즉, 스테이지 0 랭크 0은 스테이지 1 랭크 0과 통신하고 스테이지 0 랭크 1은 스테이지 1 랭크 1과 통신합니다. TP 샤드 레이아웃이 스테이지 간에 균일하므로 경계에서 크로스-랭크 교환은 발생하지 않습니다.
트랜스포트 트래픽 클래스¶
2D 라우팅 레이어는 메시의 모든 노드에 걸쳐 단일 연결 풀을 공유하지만 메시지마다 올바른 핸들러가 처리하도록 두 트래픽 클래스를 구분합니다:
| 클래스 | 피어 집합 | 핸들러 |
|---|---|---|
tp_collective | 같은 pp_stage의 모든 랭크 | TP all-reduce / reduce-scatter / all-gather |
pp_activation | 같은 tp_rank를 가진 인접 스테이지 노드 | PP 활성화 / 경계 텐서 핸드오프 |
두 클래스 모두 PipelineTensorParallelRouter를 통해 선택됩니다. 각 클래스의 전체 피어 집합은 컬렉티브 레이어가 사용하는 레지스트리 조회 (NodeRegistry::nodes_at_stage)와 활성화 전송 레이어(NodeRegistry::nodes_at_rank)에서 얻습니다.
CLI 진입점¶
src/commands/generate.rs의 검증기는 이 조합을 받아들이고 일관성 없는 토폴로지에 대해 가독성 있는 에러를 방출합니다.
클러스터 TOML과 캐시 조정¶
클러스터 TOML은 pipeline_tensor_parallel 역할(pp_tp, 2d 별칭)을 사용하며 정확히 pp_size × tp_size개의 해당 노드를 요구합니다. 각 노드는 stage와 rank를 모두 지정해야 합니다. 2×2 TOML 예시와 캐시 조정 규칙 (coordinated_2d_admission, broadcast_2d_eviction)의 전체 내용은 파이프라인 병렬화 → 텐서 병렬화와의 조합 (2D PP × TP) 를 참조하십시오.
제약¶
- 스테이지별 TP 폭은 균일: 이종 폭(예: 스테이지 0이
tp=4, 스테이지 1이tp=2)은 지원하지 않습니다. - 3D 병렬화는 범위 밖: Expert 병렬화(PP × TP × EP)는 지원하지 않습니다.