Tensor Parallel(TP)이 throughput을 선형으로 높이지 못하는 이유를 "통신 오버헤드 때문"이라고 설명하는 글은 많다. 그런데 그 오버헤드가 실제로 얼마이고, 어떤 조건에서 연산 시간을 넘어서는지를 수식과 수치로 보여주는 글은 드물다. 답은 배치 크기, GPU 수, 인터커넥트 대역폭 세 변수의 곱에 달려 있다.
TP가 발생시키는 통신: 레이어마다 두 번씩
Megatron-LM 논문(Shoeybi et al., 2019)이 정의한 TP 패턴은 지금도 vLLM, SGLang, TensorRT-LLM이 그대로 쓴다. Transformer 레이어 하나에 AllReduce가 두 번 발생한다.
MLP 블록에서는 gate/up projection을 컬럼 방향으로 쪼개고 down projection을 로우 방향으로 쪼갠다. 각 GPU가 down projection 결과를 계산한 뒤 AllReduce로 합산한다. Attention 블록에서도 동일한 구조가 반복된다: QKV projection을 분할하고 output projection 이후 AllReduce. 80개 레이어짜리 70B 모델이면 순전파 한 번에 AllReduce가 160번 발생한다.
AllReduce 비용의 수식 모델
Ring-AllReduce 알고리즘에서 GPU 1대가 주고받는 총 데이터량은 다음 식으로 계산한다.
전송 데이터량 = 2 × (N-1)/N × M
N은 GPU 수, M은 AllReduce 대상 텐서 크기(바이트)다. N이 커질수록 계수가 2에 수렴한다. N=2이면 1.0, N=4이면 1.5, N=8이면 1.75, N=16이면 1.875다. GPU를 두 배 늘릴 때마다 이 계수의 증분은 점점 작아진다.
70B 모델 구체 계산
70B LLaMA-2 계열 기준으로 hidden_size=8192, BF16(2 bytes), TP=4, batch=1:
M = 8192 × 1 × 2 = 16,384 bytes = 16 KB
전송량 = 16,384 × 2×(4-1)/4 = 24,576 bytes ≈ 24 KB
NVLink 단방향 300 GB/s(A100 SXM 기준)로 순수 대역폭만 보면:
t = 24,576 / (300 × 10⁹) ≈ 0.08 μs
이 숫자가 사실처럼 보이면 안 된다. NCCL AllReduce는 커널 런치와 동기화 오버헤드가 붙어서 소규모 텐서에서 실제 지연이 10~30 μs를 넘는다. batch=1 Decode 상황에서는 이 latency floor가 지배한다.
batch=128로 키우면 달라진다:
M = 8192 × 128 × 2 = 2,097,152 bytes = 2 MB
전송량 = 2 MB × 1.5 = 3 MB
t = 3 × 10⁶ / (300 × 10⁹) ≈ 10 μs
텐서가 수 MB 규모로 커지면 대역폭이 실제 제한 요인이 된다. NVLink에서 10 μs, PCIe(단방향 ~32 GB/s)에서는 동일 계산에 ~93 μs다.
TP=2/4/8별 AllReduce 전송량 비교 (7B, hidden_size=4096)
| batch | TP=2 (계수 1.0) | TP=4 (계수 1.5) | TP=8 (계수 1.75) |
|---|---|---|---|
| 1 | 8 KB | 12 KB | 14 KB |
| 8 | 64 KB | 96 KB | 112 KB |
| 32 | 256 KB | 384 KB | 448 KB |
| 128 | 1 MB | 1.5 MB | 1.75 MB |
TP를 2에서 4로 높이면 전송량이 1.5배가 된다. 연산이 절반으로 줄어드는 대신 통신이 더 많아진다. TP=8에서 계수는 1.75로, TP=4 대비 전송량이 17% 더 늘어나는데 연산은 또 절반이다.
연산 시간 vs 통신 시간: breakeven
Decode 단계(batch=1, seq=1)에서 각 GPU가 처리하는 GEMM은 [1, hidden_size] × [hidden_size, hidden_size/TP] 형태다. 입력 벡터가 1행이라 이 연산은 compute-bound가 아니라 memory-bound다. 가중치 행렬을 HBM에서 읽어내는 시간이 지배한다.
7B 모델, TP=4, A100(HBM 대역폭 ~2 TB/s)에서 MLP down projection 가중치 [4096, 1024]를 BF16으로 읽는 데:
t_gemm = 4096 × 1024 × 2 / (2 × 10¹²) ≈ 4.2 μs
NCCL AllReduce latency floor는 경험상 10~20 μs다. batch=1에서 통신이 연산보다 길다. TP degree를 높이면 분할된 GEMM은 더 짧아지고 AllReduce 데이터량은 늘어나므로, 통신 비율은 더 올라간다. TP=8로 가면 GEMM이 절반으로 줄어서 2 μs대가 되는데 AllReduce는 여전히 10 μs 이상이다.
Prefill(seq=2048, batch=1)에서는 계산이 뒤집힌다. GEMM 텐서가 [2048, 4096] × [4096, 1024]로 커지면서 compute-bound에 근접한다. A100 312 TFLOP/s 기준으로 이 GEMM의 이론 시간은 수백 μs다. AllReduce 10 μs는 전체의 몇 %에 불과해진다.
Overlap 전략과 그 한계
통신을 overlap하는 방법은 간단하다. AllReduce를 별도 CUDA stream에서 비동기로 시작하고, 동시에 다음 레이어의 GEMM을 돌린다. AllReduce 시간 < 다음 레이어 GEMM 시간이면 통신이 사실상 free다.
Prefill 단계에서는 이 조건이 충족된다. GEMM이 충분히 길어서 AllReduce가 그 뒤에 완전히 숨는다. Decode batch=1에서는 GEMM이 4~10 μs인데 AllReduce 커널 오버헤드만 이미 10 μs를 넘긴다. 숨길 연산보다 통신이 더 길면 overlap은 공짜 점심이 아니라 그냥 기다림이다.
MSCCL++(2025)를 쓴 실험에서 Llama3-70B Decode 지연이 평균 1.11× 개선됐다는 결과가 있는데, 뒤집어 보면 표준 NCCL 기준 AllReduce가 decode step time의 10% 수준을 차지하고 있다는 뜻이다. TokenWeave(2025)는 fine-grained overlap을 통해 이 overhead를 더 줄이려는 방향으로 연구가 이어지고 있다.
NVLink vs PCIe vs RDMA
NVLink 4세대(H100 SXM) 단방향 대역폭은 약 450 GB/s, 3세대(A100 SXM)는 약 300 GB/s다. H100 PCIe의 inter-GPU 대역폭은 약 64 GB/s로 SXM 대비 7배 낮다.
PCIe 서버에서 TP=4를 시도하면 TP=2보다 throughput이 떨어지는 임계 배치 크기가 실제로 존재한다. batch=816 수준에서 이미 AllReduce 비용이 GEMM을 초과한다. 연산이 절반으로 줄어도 통신이 PCIe 기준으로 45배 비싸지면 net gain이 없다. PCIe 환경에서 TP를 늘려야 하는 이유는 "모델이 단일 GPU에 안 올라가서"뿐이어야 한다.
NVLink 환경에서도 TP=8을 넘어서면 수확 체감이 시작된다. N=8→16으로 가면 연산은 절반이 되는데 Ring-AllReduce 계수는 1.75에서 1.875로 겨우 7% 더 증가한다. 그러나 노드 경계를 넘는 순간 상황이 급변한다. InfiniBand HDR 기준 단방향 대역폭은 약 25 GB/s로 NVLink 대비 10배 이상 낮다. 노드 간 TP는 AllReduce 비용이 수십 μs에서 수백 μs로 뛰면서 거의 모든 workload에서 역효과다.
통신 비율을 직접 측정하는 법
TP degree 선택의 기준은 "모델이 단일 GPU에 올라가는가"가 아니라 "통신이 step time의 몇 %인가"다. 직접 측정하는 가장 빠른 방법:
# NCCL 통신 로그 활성화
export NCCL_DEBUG=INFO
export NCCL_DEBUG_SUBSYS=COLL
# nsys로 NCCL 커널 타이밍 추출
nsys profile --trace=cuda,nvtx \
--output=profile_tp4 \
python -m vllm.entrypoints.openai.api_server \
--model meta-llama/Llama-2-70b-hf \
--tensor-parallel-size 4
# 프로파일 분석 (ncclKernel_* 커널 시간 확인)
nsys stats profile_tp4.nsys-rep --report gputrace
ncclKernel_*이 차지하는 시간을 전체 GPU 활성 시간으로 나누면 통신 비율이 나온다. 이 수치가 15%를 넘으면 TP를 낮추거나 인터커넥트 업그레이드를 검토해야 한다.
모델 크기·환경별 TP 권장 범위를 정리하면:
| 환경 | 모델 | 우선순위 | 권장 TP |
|---|---|---|---|
| NVLink (A100/H100 SXM) | 7B | Decode latency | TP=1~2 |
| NVLink (A100/H100 SXM) | 7B | Throughput | TP=2~4 |
| NVLink (A100/H100 SXM) | 70B | Decode latency | TP=4 |
| NVLink (A100/H100 SXM) | 70B | Throughput | TP=8 |
| PCIe (H100/A100 PCIe) | 70B | 모든 workload | TP=4 (불가피한 경우) |
| 노드 간 RDMA | 70B+ | Prefill 우선 | Pipeline Parallel 혼용 권장 |
7B 모델을 NVLink 8-GPU에서 TP=8로 돌리는 건 가장 흔한 실수다. 단일 GPU에도 올라가는 모델에 TP를 걸면 decode latency가 늘어난다. 줄어든 연산보다 AllReduce 오버헤드가 크기 때문이다.
남은 문제는 Decode 단계에서 batch를 키울수록 통신과 연산이 어느 지점에서 교차하는지의 정확한 측정이다. 이 breakeven batch size는 모델 아키텍처, GPU 세대, NCCL 버전에 따라 달라지며 프로파일 없이 추정하기 어렵다. 새 하드웨어를 도입하거나 TP degree를 바꿀 때마다 nsys 프로파일링을 한 번 돌리는 게 가장 확실하다.