메모리 예산을 손으로 세우기
한 줄 요약
GPU 메모리 예산은 세 항목이다. 모델 가중치, KV 캐시, 그리고 활성화와 오버헤드. 두 번째가 가장 크고 가장 자주 무시된다.
왜 이게 필요했나
"A100 80GB 한 장에 70B 모델이 들어가나요"라는 질문에 답하려면 계산이 필요합니다. 가중치만 보면 fp16 기준 140GB 라 안 들어갑니다. 4비트로 양자화하면 약 35GB 라 들어갈 것 같습니다. 그런데 KV 캐시를 빼먹었습니다.
계산을 안 하고 시작하면 배포 당일에 OOM 을 만납니다. 그리고 OOM 은 부하가 몰릴 때 나므로 가장 나쁜 시점에 납니다.
어떻게 동작하나
모델 가중치는 파라미터 수 곱하기 dtype 바이트입니다. 7B fp16 이면 14GB, 70B fp16 이면 140GB 입니다. 양자화하면 이 값이 줄어듭니다 — INT8 은 절반, INT4 는 4분의 1 수준입니다.
KV 캐시 공식은 이렇습니다.
KV 바이트 = 2 x 레이어 수 x 은닉 차원 x 시퀀스 길이 x 배치 x dtype 바이트
앞의 2는 키와 값 두 개라는 뜻입니다. 7B(32층, 은닉 4096) fp16 기준으로 계산해 보면 이렇습니다.
| 컨텍스트 | 배치 1 | 배치 8 |
|---|---|---|
| 4K | 2 GiB | 16 GiB |
| 32K | 16 GiB | 128 GiB |
| 128K | 64 GiB | 512 GiB |
128K 컨텍스트에 배치 8 이면 512GiB 입니다. 가중치 14GB 짜리 모델을 돌리는 데 KV 캐시가 512GB 입니다. 롱 컨텍스트가 왜 어려운지가 이 표 하나에 있습니다.
줄이는 방법이 셋입니다. GQA 는 키·값 헤드를 그룹으로 공유해 8그룹이면 8분의 1 수준으로 줄입니다. KV 캐시 양자화는 dtype 바이트를 줄여 INT8 이면 절반, INT4 면 4분의 1 입니다. PagedAttention 은 크기 자체는 안 줄이지만 단편화를 없애 실사용 효율을 올립니다.
이것들을 겹치면 큰 절감이 됩니다. 저자의 예시에서 기본 1,280GB 가 GQA-8 로 320GB, INT8 로 160GB, PagedAttention 실사용 기준 약 128GB, INT4 까지 가면 80GB 가 됩니다.
현장에서 만나는 모습
동시 요청 수를 정하는 계산이 실무에서 가장 자주 필요합니다. GPU 총 메모리에서 가중치와 오버헤드를 빼면 KV 캐시 예산이 나오고, 그것을 요청 하나당 KV 크기로 나누면 최대 동시 요청 수가 나옵니다. 이 값이 max_num_seqs 의 상한이고, 용량 계획의 출발점입니다.
양자화 선택도 이 계산에서 나옵니다. GPTQ 와 AWQ 는 가중치만 양자화하고, KV 캐시 양자화는 별도 설정입니다. 두 가지를 헷갈리면 "4비트로 바꿨는데 메모리가 별로 안 줄었다"가 됩니다 — 컨텍스트가 긴 워크로드에서는 KV 캐시가 지배적이기 때문입니다.
예산을 세우는 식
GPU 메모리는 세 몫으로 갈립니다. 순서대로 계산하면 동시 처리 가능한 요청 수가 나옵니다.
1. 가중치 = 파라미터 수 × (비트수 ÷ 8)
7B × 2바이트(FP16) = 14.0 GB
7B × 0.5바이트(INT4) = 3.5 GB
2. 여유 = 활성화 + 단편화 ≈ 전체의 5~10%
3. KV 캐시로 쓸 수 있는 몫 = 전체 − 가중치 − 여유
KV 캐시 한 토큰이 먹는 크기는 이렇습니다.
토큰당 = 2(K와 V) × 레이어 수 × KV 헤드 수 × 헤드 차원 × 정밀도 바이트
예: Llama-3 8B (32레이어, KV헤드 8, 헤드차원 128, FP16)
= 2 × 32 × 8 × 128 × 2 = 131,072 바이트 ≈ 128 KB/토큰
GQA(Grouped-Query Attention)가 여기서 큰 차이를 만듭니다. KV 헤드가 32개면 512KB/토큰이지만 8개면 128KB 입니다. 최근 모델이 GQA 를 쓰는 이유가 이것입니다.
24GB 카드, 8B 모델 FP16:
가중치 16GB + 여유 2GB → KV 로 6GB
6GB ÷ 128KB = 약 49,000 토큰
→ 문맥 4K 면 동시 12요청, 문맥 8K 면 6요청
이 계산이 알려 주는 것
- 문맥을 두 배로 열면 동시 처리량이 절반 입니다. "혹시 모르니 32K 로 열어 두자" 는 처리량을 8분의 1로 만드는 결정입니다.
- 양자화는 가중치만 줄입니다. INT4 로 가중치를 3.5GB 로 줄이면 KV 로 쓸 몫이 크게 늘어 동시 처리량이 몇 배가 됩니다. 품질 손실보다 이 이득이 큰 경우가 많습니다.
- KV 캐시도 양자화할 수 있습니다. FP8 KV 캐시는 용량을 절반으로 줄이는데, 품질 영향이 가중치 양자화보다 작다는 보고가 많습니다.
넘쳤을 때 무슨 일이 일어나나
vLLM 은 KV 가 부족하면 선점(preemption) 합니다. 진행 중인 요청 하나를 골라 캐시를 버리고, 나중에 처음부터 다시 계산합니다.
로그: Sequence group ... is preempted by PreemptionMode.RECOMPUTE
이 로그가 자주 보이면 동시성이 용량을 넘은 것입니다. 지연이 튀는데 GPU 사용률은 높게 나와 "잘 쓰고 있다" 로 오독하기 쉽습니다. 선점 횟수를 지표로 두는 것 이 이 상태를 알아채는 방법입니다.
다음 실습에서 할 것
공식을 코드로 구현하고 여러 시나리오를 계산합니다. 7B 4K 가 2GiB 임을 검증하고, 128K 를 계산하고, GQA 와 INT8 을 적용하고, 마지막에 80GB GPU 에서 가능한 최대 동시 요청 수를 구합니다.