01. 단일 GPU로 부족해지는 이유
이 장의 핵심 질문
- "GPU를 더 붙이자"는 말은 사실 세 개의 서로 다른 문제를 뭉친 것이다. 어떤 세 가지인가?
- capacity·single-request latency·traffic capacity 한계는 각각 어떤 분산 전략이 푸는가?
- 이 세 방향을 섞으면 왜 이후 모든 장이 혼란스러워지는가?
앞 과정과의 연결 — 선수 과정 ②에서 단일 엔진의 request·prefill/decode·KV 캐시·batching·scheduler를 다뤘다. 이 장은 그 "단일 엔진"이 한계에 닿는 지점을 셋으로 분리하고, 이후 16개 장의 배포 척추를 세운다.
먼저: "부족하다"는 말은 한 가지 뜻이 아니다
누군가 "GPU 한 장으로는 부족하다"고 말할 때, 실제로는 셋 중 하나(또는 여럿)를 의미한다. 이 셋은 원인·증상·해법이 모두 다르다. 이 장의 전부는 이 셋을 떼어놓는 것이다.
일상 비유로 시작하자. 식당 하나를 운영한다고 하자.
- 주방이 너무 작아 요리 자체가 안 들어간다 — 대형 웨딩 케이크가 오븐에 물리적으로 안 들어가는 상황. → 오븐을 여러 대로 나눠 케이크를 쪼개 굽는다.
- 요리 한 접시를 내는 데 너무 오래 걸린다 — 손님 한 명이 주문한 코스가 나오기까지의 시간이 길다. → 조리 단계를 나눠 병렬로 처리하거나, 준비 작업과 마무리 작업을 다른 스테이션으로 분리한다.
- 손님이 너무 많아 줄이 밀린다 — 요리 하나하나는 빠른데 밀려드는 주문 수를 감당 못 한다. → 같은 식당을 여러 개 복제하고, 손님을 여러 지점으로 나눠 보낸다.
세 문제는 완전히 다르다. 오븐이 작은 문제(첫째)를 식당을 복제해서(셋째) 풀 수는 없다. 이 구분이 분산 인프라 전체의 출발점이다.
세 방향의 한계
1) Capacity 한계 — "모델이 한 장에 안 들어간다"
정의. 모델의 가중치(weights) + 실행 중 필요한 KV 캐시(KV cache) + 활성값(activations)이 GPU 한 장의 메모리(HBM)를 넘어서는 상황. 요청이 많고 적고와 무관하게, 단 하나의 요청도 물리적으로 실행할 수 없다.
- 원인: 모델 크기(파라미터 수 × 파라미터당 바이트) 자체가 device 메모리보다 크다. 선수 과정 ①의 "정밀도 × 파라미터 수 = 메모리" 관계가 여기서 벽이 된다.
- 증상: out-of-memory. 배치 크기를 1로 줄여도 안 올라간다.
- 해법 방향: 모델을 여러 device에 쪼갠다(shard) — 텐서 병렬화(TP, 03장), 파이프라인 병렬화(PP, 04장), 전문가 병렬화(EP, 05장). 이것이 모델 병렬화(model parallelism) 다.
📌 핵심 — capacity 한계는 메모리에 안 들어가는 문제다. 트래픽과 무관하다. 해법은 "나눈다(shard)".
2) Single-request latency 한계 — "요청 하나가 너무 느리다"
정의. 모델이 device에 들어가긴 하는데, 요청 하나의 응답 시간(예: 첫 토큰까지의 시간 TTFP, 토큰당 시간 TPOT — 선수 과정 ② 참조)이 목표보다 길다. 큐가 비어 있어도, 동시 요청이 하나뿐이어도 느리다.
- 원인: 단일 요청의 연산·메모리 이동이 device 한 장의 처리 속도로는 목표 latency를 못 맞춘다. 또는 prefill(긴 프롬프트 처리)과 decode(토큰 생성)가 같은 device에서 서로 간섭한다.
- 증상: 부하가 낮은데도 p50 latency가 목표를 초과. 배치를 키워도 개별 요청은 빨라지지 않는다(배치는 처리량을 위한 것이지 단일 latency를 위한 것이 아니다 — 선수 과정 ②).
- 해법 방향: 요청 하나의 작업을 여러 device로 나눠 병렬 처리(TP는 latency도 낮출 수 있음, 03장)하거나, 간섭을 분리한다(PD 분리, 07장). 단, 나눔에는 통신 비용이 따라붙는다(06장).
📌 핵심 — latency 한계는 한 요청이 느린 문제다. 부하가 0이어도 존재한다. 해법은 "나눠서 빨리" 또는 "간섭을 떼어낸다".
3) Traffic capacity 한계 — "요청 수를 못 감당한다"
정의. 모델도 들어가고 요청 하나의 latency도 괜찮은데, 동시에 들어오는 요청 수(throughput 요구)를 한 device가 감당 못 한다. 큐가 쌓이고 대기 시간이 늘어난다.
- 원인: 초당 요청 수(또는 초당 토큰 수) 요구가 device 한 장의 처리 상한을 넘는다.
- 증상: 부하가 오르면 큐 대기(queueing delay)가 급증하고, tail latency(p99 등)가 폭발한다. 요청 하나만 보내면 여전히 빠르다.
- 해법 방향: 같은 (병렬화된) 엔진을 여러 벌 복제(replicate) 하고 — 데이터 병렬화(DP, 04장) — 요청을 여러 복제본에 나눠 보낸다(라우팅·로드밸런싱, 09장). 부하가 오르내리면 복제 수를 오토스케일링(09장)한다.
📌 핵심 — traffic 한계는 요청이 많은 문제다. 요청 하나만 보면 멀쩡하다. 해법은 "복제(replicate)하고 나눠 보낸다".
세 방향을 한눈에
flowchart TD
Q{"'GPU 한 장으로<br/>부족하다'"}
Q -->|"모델이 메모리에<br/>안 들어감"| C1["① Capacity 한계<br/>부하와 무관"]
Q -->|"요청 하나가<br/>목표보다 느림"| C2["② Latency 한계<br/>부하 0에도 존재"]
Q -->|"요청 수를<br/>감당 못 함"| C3["③ Traffic 한계<br/>요청 하나는 멀쩡"]
C1 -->|"나눈다 shard"| S1["모델 병렬화<br/>TP·PP·EP<br/>(03·04·05장)"]
C2 -->|"나눠서 빨리 /<br/>간섭 분리"| S2["TP + PD 분리<br/>(03·07장)"]
C3 -->|"복제 replicate +<br/>나눠 보냄"| S3["DP + 라우팅·<br/>오토스케일링<br/>(04·09장)"]
S1 -.->|"대가"| X1["collective 통신<br/>(02·03장)"]
S2 -.->|"대가"| X2["KV 전송·큐·tail<br/>(06·07·08장)"]
S3 -.->|"대가"| X3["로드 불균형·<br/>상태 관리(09장)"]
classDef question fill:#fff3c4,stroke:#d4a017,color:#000
classDef cap fill:#e9d8fd,stroke:#7c3aed,color:#000
classDef lat fill:#cfe2ff,stroke:#2563eb,color:#000
classDef traf fill:#c4f1f4,stroke:#0891b2,color:#000
classDef cost fill:#ffd6d6,stroke:#dc2626,color:#000
class Q question
class C1,S1 cap
class C2,S2 lat
class C3,S3 traf
class X1,X2,X3 cost
| 방향 | 무엇이 부족한가 | 부하 0일 때도? | 요청 1개만 보면? | 해법의 핵심 동사 | 주로 다루는 장 |
|---|---|---|---|---|---|
| ① Capacity | device 메모리 | 있음 | 실행 불가 | 나눈다(shard) | 03·04·05 |
| ② Latency | 단일 요청 속도 | 있음 | 느림 | 나눠 빨리 / 분리 | 03·07 |
| ③ Traffic | 처리량(요청 수) | 없음 | 빠름 | 복제(replicate)·라우팅 | 04·09 |
⚠️ 경계 혼동 주의 — 이 세 축을 섞는 것이 이 분야 오해의 최대 원천이다. "TP를 쓰면 빨라진다"는 말은 latency 문제(②)에는 부분적으로 맞지만, traffic 문제(③)에는 엉뚱한 답이다(요청 수는 TP로 늘지 않는다 — 오히려 통신 오버헤드로 처리량이 줄 수도 있다). 반대로 "복제하면 된다"는 말은 traffic(③)에는 맞지만 capacity(①)에는 무의미하다(안 들어가는 모델은 복제해도 안 들어간다). 어떤 한계를 말하는지 먼저 못 박아라.
이 안내서의 척추 — 하나의 배포 시나리오
이 안내서는 코드 프로젝트를 쌓는 대신, 하나의 배포를 따라간다. 출발점은 이렇다(구체 수치는 예시이며 특정 제품 사양이 아니다):
배포 D0 — 어떤 팀이 중형 LLM을 GPU 한 장, 엔진 한 벌(replica 1개)로 서빙하고 있다. 처음엔 잘 돌았지만 세 방향에서 동시에 압력을 받기 시작했다:
- 더 큰 모델로 바꾸려니 메모리에 안 들어간다 (① capacity)
- 긴 프롬프트 요청이 들어오면 짧은 요청들의 응답까지 느려진다 (② latency — prefill/decode 간섭)
- 사용자가 늘면서 피크 시간에 큐가 밀린다 (③ traffic)
이후 각 장에서 이 배포 D0에 한 겹씩 얹는다:
flowchart LR
D0["D0<br/>1 GPU·1 replica"] --> D1["+ 모델 병렬화<br/>TP·PP (03·04장)"]
D0 --> D2["+ 복제·라우팅<br/>DP (04·09장)"]
D0 --> D3["+ PD 분리<br/>(07장)"]
D1 --> D4["+ 다중 노드·통신<br/>(02·06장)"]
D2 --> D4
D3 --> D5["+ KV 전송·계층화<br/>(08장)"]
D4 --> D6["+ 서빙 계층<br/>(12장)"]
D5 --> D6
D6 --> D7["+ SLO·capacity 계약<br/>(10장)"]
D7 --> BRIEF["근거 기반<br/>기술 브리프 (17장)"]
classDef base fill:#cfe2ff,stroke:#2563eb,color:#000
classDef mid fill:#c4f1f4,stroke:#0891b2,color:#000
classDef end1 fill:#d1f0d1,stroke:#16a34a,color:#000
class D0 base
class D1,D2,D3,D4,D5,D6 mid
class D7,BRIEF end1
핵심은 D0의 압력을 세 방향으로 미리 분리해 둔 것이다. 앞으로 어떤 전략을 얹든, "이건 세 방향 중 어느 것을 푸는가?"를 먼저 묻는다.
세 방향은 독립이 아니다 — 절충이 생기는 이유
세 축을 분리하라고 했지만, 현실에서는 서로 얽힌다. 한 방향을 풀면 다른 방향에 비용이 생긴다. 이것이 이후 모든 장에 등장하는 ⚖️ 절충의 뿌리다.
- capacity를 풀려고 모델을 여러 device에 나누면(①→shard), device 사이에 collective 통신이 생긴다(02·03장). 통신은 공짜가 아니므로 latency(②)와 throughput(③)에 영향을 준다.
- latency를 풀려고 prefill/decode를 분리하면(②→PD 분리, 07장), 두 단계 사이에 KV 캐시를 전송해야 한다(08장). 전송은 새로운 지연·실패 경로다.
- traffic을 풀려고 복제·오토스케일링하면(③), 상태(KV·세션) 를 어디에 둘지, 부하를 어떻게 고르게 나눌지가 새 문제가 된다(09장).
⚖️ 절충 (이 안내서 전체의 예고편) — 분산의 거의 모든 전략은 한 방향의 한계를 풀면서 다른 방향에 새 비용을 만든다. "더 많이 나누고 더 많이 분리하면 항상 낫다"는 참이 아니다. 각 전략마다 줄이는 것과 새로 만드는 비용을 나란히 적는 것이 이 과정의 반복 훈련이다.
왜 이 분리가 "증거를 읽는" 훈련의 시작인가
이후 장에서 논문·문서·배포 보고서를 읽을 때, 성능 주장은 거의 항상 세 방향 중 하나에 대한 것이다. 예를 들어 "우리 시스템이 X% 빨라졌다"는 주장을 만나면 먼저 물어야 한다: 어느 방향의 개선인가? 단일 요청 latency(②)인가, 총 처리량(③)인가, 아니면 애초에 안 돌던 모델을 돌리게 된 것(①)인가? 방향이 다르면 분모가 다르고, 분모가 다르면 직접 비교할 수 없다.
🔬 증거 읽기 (첫 연습) — 성능 주장을 만나면 세 가지를 분리하라: (a) 어느 한계(capacity/latency/traffic)를 다루는 주장인가, (b) 무엇이 분자이고 무엇이 분모인가(요청 하나당? 초당 총량? device당?), (c) 어떤 조건(모델·워크로드·하드웨어)에서 측정됐는가. 이 세 가지가 다르면 두 주장은 같은 표에 나란히 놓을 수 없다. 15장에서 이 판정을 본격적으로 다룬다.
이 장에서 배운 것
- "GPU가 부족하다"는 말은 capacity(안 들어감) · latency(한 요청이 느림) · traffic(요청 수를 못 감당) 세 개의 다른 문제를 뭉친 것이다.
- 각 방향의 해법 동사가 다르다: capacity는 나눈다(shard), latency는 나눠 빨리 / 분리한다, traffic은 복제(replicate)하고 나눠 보낸다.
- 세 축을 섞으면(예: traffic 문제에 "TP를 쓰자") 엉뚱한 해법이 나온다. 어떤 한계인지 먼저 못 박는 것이 이후 모든 장의 전제다.
- 한 방향을 풀면 다른 방향에 새 비용이 생긴다(통신·KV 전송·상태 관리). 분산의 모든 전략은 절충이다.
- 성능 주장은 거의 항상 세 방향 중 하나에 관한 것이다. 어느 방향인지, 분자·분모·조건이 무엇인지를 분리하는 것이 증거 읽기의 출발점이다.
🔎 이 장의 '지지하지 않는 결론' + 'unknown으로 남겨야 할 것'
- 지지하지 않는 결론 — "GPU를 더 붙이면 무엇이든 나아진다"는 이 장에서 지지되지 않는다. 이 장이 지지하는 것은 오직 "부족의 종류에 따라 해법이 다르다"는 것뿐이다. 어떤 전략이 얼마나 개선하는지는 조건에 달렸고 뒤 장에서 조건과 함께 다룬다.
- unknown으로 남길 것 — 배포 D0에서 "긴 프롬프트가 짧은 요청을 느리게 한다"고 했지만, 그 간섭의 정확한 크기는 모델·프롬프트 길이·배치 정책에 따라 다르며 여기서는
unknown이다. 구체 수치는 측정 조건 없이는 옮기지 않는다.
✍️ 확인 문제
- 어떤 팀이 "요청 하나만 보내면 200ms면 되는데, 피크 시간엔 3초가 걸린다"고 한다. 이것은 세 방향 중 어느 한계인가? 그 근거는? 그리고 이 한계에 적합하지 않은 해법 하나를 골라 왜 부적합한지 말하라.
- (조건/분모 유형) A 시스템은 "요청당 latency를 40% 낮췄다"고 하고, B 시스템은 "초당 처리 토큰 수를 40% 늘렸다"고 한다. 두 "40%"를 같은 표에 순위로 나란히 놓을 수 있는가? 없다면 무엇이 다른가?
- capacity 한계를 "복제(replicate)"로 풀려는 시도가 왜 원리적으로 실패하는지, 그리고 latency 한계를 순수 복제로 풀 수 있는지(요청 하나의 응답 시간 관점에서) 각각 설명하라.
다음 장에서는, 모델을 여러 device에 나누는 순간 반드시 따라오는 device 사이의 연결 — PCIe·NVLink·InfiniBand·Ethernet — 을 link·fabric·software scope로 나눠 본다.
다음 → 02. PCIe, NVLink, InfiniBand, Ethernet — 무엇이 무엇과 어떻게 연결되는가