70B 모델을 처음 서빙할 때 가장 먼저 받는 질문은 "GPU 몇 장?"이고, 그 다음이 "어떻게 쪼갤?"이다. 이 두 번째 질문이 생각보다 복잡하다. GPU 8장을 가지고 있어도 TP=8로 묶을지, TP=4×PP=2로 구성할지, TP=4 인스턴스를 두 개 띄울지에 따라 TTFT와 처리량이 반대 방향으로 움직인다.
단일 GPU가 먼저 막히는 지점
Llama-3 70B를 BF16으로 올리면 가중치만 140GB다. H100 하나(80GB)에는 들어가지 않아서 두 장이 최소다. 405B는 BF16 기준 810GB라서 8xH100으로도 FP8으로 바꿔야 겨우 들어간다. 메모리 한계가 분산을 강제하는 첫 번째 이유다.
두 번째 이유는 다르다. 모델이 단일 GPU에 올라가더라도 처리량을 높이거나 레이턴시를 낮추려는 목적으로 분산을 택한다. 이 두 목적은 요구하는 분산 전략이 다르다. 메모리 한계를 해소하는 분산(모델 병렬)과 처리량을 키우는 분산(데이터 병렬)을 같은 전략으로 풀려고 하면 어디서 손해 보는지조차 파악하기 어렵다.
텐서 병렬(TP): 레이어 안을 수평으로 자른다
Megatron-LM(Shoeybi et al., 2019)이 정립한 텐서 병렬의 핵심은 Transformer 레이어 하나를 여러 GPU에 나눠 동시에 계산하는 것이다. Attention의 QKV projection과 FFN의 첫 번째 linear layer는 column-parallel로, output projection과 FFN의 두 번째 linear layer는 row-parallel로 쪼갠다.
column-parallel은 weight matrix를 열 방향으로 나눠 각 GPU가 독립적으로 partial output을 계산한다. row-parallel은 각 GPU가 partial sum을 계산한 뒤 all-reduce로 합산한다. attention 블록과 FFN 블록에서 각각 한 번씩, 레이어당 총 두 번의 all-reduce가 발생한다.
이 all-reduce가 TP의 핵심 변수다. ring-all-reduce 기준으로 GPU당 이동하는 데이터는 약 2 × hidden_size × bytes_per_element다. Llama-3 70B(hidden_size=8192, BF16=2바이트)라면 all-reduce 한 번에 약 32KB, 레이어당 64KB, 80개 레이어 전체를 통과하면 decode 한 스텝에 약 5MB가 네트워크를 타게 된다.
여기서 NVLink 유무가 결정적으로 갈린다. H100 SXM의 NVLink 4.0은 GPU당 양방향 900GB/s를 제공한다. PCIe 5.0 x16은 단방향 64GB/s다. 대역폭이 14배 차이난다. L40S처럼 NVLink 없이 PCIe로만 연결된 서버에서 TP=4를 돌리면 추론 시간의 40~50%가 통신에 사라진다는 측정 결과가 있다. vLLM 공식 문서도 NVLink 없는 환경에서는 TP 대신 PP를 명시적으로 권장한다.
TP가 커질수록 효율이 무너진다
이론상 TP=N이면 N배 빨라져야 하지만 실측은 다르다.
| TP 크기 | 이론 속도 향상 | 실제 속도 향상 | 효율 |
|---|---|---|---|
| TP=2 | 2.0× | 1.7–1.9× | 85–95% |
| TP=4 | 4.0× | 2.8–3.4× | 70–85% |
| TP=8 | 8.0× | 4.5–6.0× | 56–75% |
8xH100, Llama-3 70B FP8 환경에서 vLLM GitHub 이슈 #8089에 공개된 수치를 보면 문제가 더 극적이다.
| 설정 | 처리량 | TTFT | 토큰당 레이턴시 |
|---|---|---|---|
| TP=4 단일 인스턴스 | 3.25 req/s | 618ms | 65ms |
| TP=4 × 2 인스턴스 (로드밸런싱) | 6.33 req/s | 584ms | 66ms |
| TP=8 단일 인스턴스 | 5.30 req/s | 824ms | 259ms |
TP=8은 TP=4 두 인스턴스 로드밸런싱보다 처리량이 16% 낮고 TTFT는 35% 높다. 토큰당 레이턴시는 거의 4배다. all-reduce 지연이 계산 시간을 추월하면 GPU를 더 쓸수록 오히려 느려진다. "NVLink 환경이면 TP=8이 무조건 낫다"는 직관이 성립하지 않는 사례다.
파이프라인 병렬(PP): 레이어를 수직으로 쌓는다
PP는 Transformer 레이어를 여러 스테이지로 나눠 각 GPU가 서로 다른 레이어 집합을 담당하는 방식이다. GPU 간 통신은 스테이지 경계에서만 발생하며, 앞 스테이지의 activation을 다음 스테이지로 넘기는 단순 P2P transfer다. all-reduce가 없으므로 NVLink 없이 PCIe로도 감당할 수 있다.
문제는 파이프라인 버블이다. PP=4라면 GPU 4대를 순서대로 거쳐야 하는데, 첫 마이크로배치가 stage 4를 처리하는 동안 stage 1 GPU는 놀고 있다. GPipe(Huang et al., 2019)는 이 유휴 시간을 마이크로배치 여러 개로 채우는 방식을 제안했다. 버블 비율 공식은 다음과 같다.
bubble_fraction = (pp_stages - 1) / (pp_stages - 1 + num_microbatches)
PP=4에 마이크로배치 8개면 버블 비율은 3/(3+8) ≈ 27%다. 마이크로배치를 늘릴수록 버블이 줄지만, 그만큼 배치 크기도 커진다는 뜻이다.
decode 단계에서 PP가 특히 불리한 이유
decode 단계는 토큰을 하나씩 자기회귀적으로 생성한다. 한 스텝에 처리하는 유효 마이크로배치는 사실상 1개다. PP=4에 마이크로배치=1이면:
3 / (3 + 1) = 75%
GPU 4대 중 3대가 놀고 있다. 처리량을 올리려면 concurrency를 충분히 확보해 여러 요청이 동시에 스테이지를 채워줘야 한다. 요청이 드문드문 들어오는 실시간 대화 환경에서 PP를 쓰면 GPU 활용률이 25%까지 내려간다.
prefill과 decode, 어떤 전략이 유리한가
두 단계의 연산 특성이 다르다.
prefill은 프롬프트 전체를 한 번에 처리한다. 입력 시퀀스 1024 토큰이면 hidden_state 행렬은 [1024 × 8192]다. attention과 FFN 모두 큰 행렬 곱셈이 지배하고 GPU는 compute-bound 상태다. TP는 이 큰 행렬을 여러 GPU에 나눠 동시에 계산하므로 prefill 레이턴시를 직접 줄인다. all-reduce 비용이 크더라도 NVLink 대역폭이 충분하면 계산 시간 단축이 통신 비용을 상쇄한다.
decode는 다르다. 각 스텝에서 처리하는 hidden_state는 [batch_size × 8192]인데, 배치 크기가 작으면 [1 × 8192]짜리 벡터-행렬 연산이 레이어마다 반복된다. 이 연산은 memory-bandwidth bound다. 계산 자체는 빠르게 끝나는데 all-reduce가 그것보다 오래 걸리는 상황이 생긴다. TP를 늘릴수록 all-reduce 빈도와 부하가 늘어나는데 compute 절감 효과는 그보다 작다. PP는 스테이지 경계 통신이 드물고 각 GPU가 담당 레이어를 전담 처리하므로, 높은 concurrency에서 decode 처리량이 유리하다.
두 단계의 연산 밀도 차이가 분산 전략 선택을 가르는 축이다. prefill-decode 분리 서빙 아키텍처가 의미 있는 이유가 여기 있다. 단계를 물리적으로 분리하면 prefill fleet에는 TP를 공격적으로, decode fleet에는 PP 비중을 높이고 concurrency로 버블을 채우는 식으로 각각에 맞는 조합을 독립적으로 설정할 수 있다.
DP와 EP — 별도 축이 필요한 경우
데이터 병렬(DP)은 모델을 여러 replica로 복제해 요청을 분산한다. GPU 메모리가 충분하고 처리량을 더 키우고 싶을 때 선택지다. 단점은 KV 캐시가 replica 간 공유되지 않아 prefix 캐시 히트율이 replica 수에 반비례한다. 긴 시스템 프롬프트를 재사용하는 RAG 파이프라인에서 DP를 무작정 늘리면 캐시 효과가 분산돼 TTFT가 오히려 늘어날 수 있다.
Expert 병렬(EP)은 MoE 모델 전용이다. DeepSeek-V3나 Mixtral처럼 레이어마다 expert를 선택적으로 활성화하는 구조에서 expert 자체를 GPU별로 분산 배치한다. 특정 expert에 토큰이 몰리는 부하 불균형 문제와 all-to-all 통신 부담이 추가된다.
같은 GPU 8장, TP×PP 조합을 달리 하면 어떻게 바뀌나
8xH100 단일 노드 기준으로 조합을 바꿔가며 측정한 수치를 정리하면:
| 조합 | 주요 특성 | TTFT | 처리량 |
|---|---|---|---|
| TP=8, PP=1 | all-reduce 최대 부하 | 높음 (824ms) | 중간 (5.30 req/s) |
| TP=4 × 2 인스턴스 | 사실상 DP=2, KV 캐시 분리 | 낮음 (584ms) | 최고 (6.33 req/s) |
| TP=4, PP=2 | 노드 간 확장 시 선택지 | TP=4와 유사 | PP 버블만큼 감소 |
Llama-3 70B FP8 환경에서는 TP=8 단일 인스턴스가 TP=4 두 인스턴스보다 처리량에서 진다. GPU를 하나의 큰 병렬 그룹으로 묶을수록 좋다는 직관이 NVLink 환경에서도 통하지 않는 사례다.
405B처럼 BF16 810GB를 담아야 하는 극대형 모델은 다르다. 8xH100가 최소 단위고 TP=8이 사실상 강제된다. 그 경우 가중치를 FP8로 낮춰 405GB로 줄이거나, 다중 노드에 PP를 추가해 메모리를 더 확보하는 방향이 현실적이다.
vLLM에서 TP와 PP를 설정하는 방법:
# 단일 노드 8GPU, TP=8 단일 인스턴스
vllm serve meta-llama/Llama-3-70b-instruct \
--tensor-parallel-size 8
# 단일 노드 TP=4 두 인스턴스 (별도 프로세스, 로드밸런서 앞에 배치)
vllm serve meta-llama/Llama-3-70b-instruct \
--tensor-parallel-size 4
# 2노드, 노드당 TP=4, 노드 간 PP=2 확장
vllm serve meta-llama/Llama-3-70b-instruct \
--tensor-parallel-size 4 \
--pipeline-parallel-size 2
PP=2 이상 구성에서 vLLM은 Ray를 통해 노드 간 worker를 조율하며, pipeline stage 경계의 activation 전달은 NCCL P2P 통신으로 처리한다.
실무 선택 기준
조건별로 정리하면:
-
모델이 단일 GPU에 올라갈 때: 처리량 목표라면 DP(복제 인스턴스 여러 개)가 가장 단순하고 효과적이다. 레이턴시를 낮추려면 TP=2 수준에서 멈추는 게 낫다. TP=4부터 효율이 70~85%로 내려간다.
-
NVLink 있는 서버(H100 SXM 등): TP를 최대 8까지 시도해 보되, TP=4 두 인스턴스와 처리량을 직접 비교한다. TP=8이 지는 사례가 흔하다.
-
NVLink 없는 서버(L40S, A10G 등): TP=2로 제한하고 나머지를 PP로 채운다. PCIe 환경에서 TP=4 이상은 통신이 추론 시간의 절반을 잡아먹는다.
-
레이턴시 SLA가 엄격할 때: PP를 최소화한다. decode 단계에서 PP=4면 버블 비율이 75%까지 오른다. 낮은 concurrency와 PP의 조합은 최악이다.
-
처리량 극대화가 목표일 때: PP로 스테이지를 늘리되 concurrency를 높게 유지해 버블을 희석시킨다. 마이크로배치를 충분히 쌓지 못하면 PP 확장이 GPU 낭비로 끝난다.
-
다중 노드 확장: 노드 내는 NVLink로 TP, 노드 간은 InfiniBand나 이더넷으로 PP를 구성한다. TP를 노드 경계를 넘겨 쓰는 것은 대역폭이 급격히 줄어 실질적으로 선택하기 어렵다.
TP=8 단일 인스턴스가 TP=4 두 인스턴스에 처리량에서 밀리는 현상은 NVLink 환경에서도 실제로 관찰된다. concurrency와 모델 크기에 따라 최적 조합이 달라지므로, 조합을 바꿔가며 TTFT와 처리량을 직접 측정하는 것이 유일하게 믿을 수 있는 방법이다.