KV Cache가 VRAM을 잠식하고 있습니다 — 메모리가 고갈되기 전에 이를 추정하는 방법
요약
LLM 추론 시 VRAM 부족의 주요 원인인 KV Cache의 메모리 점유를 계산하는 공식과 그 중요성을 설명합니다. 컨텍스트 길이와 배치 크기에 따라 동적으로 증가하는 KV Cache의 특성을 분석하고 메모리 고갈을 예측하는 방법을 다룹니다.
핵심 포인트
- KV Cache 메모리 추정 공식 제공
- 긴 컨텍스트와 높은 배치 크기에서 KV Cache가 가중치보다 큰 병목이 됨
- Speculative Decoding 사용 시 추가적인 KV Cache 비용 발생
- 동적 메모리 관리를 위한 컨텍스트 및 배치 크기 조절의 필요성
모든 LLM 추론 엔지니어는 결국 이 벽에 부딪히게 됩니다.
모델을 배포했고 테스트 단계에서는 잘 작동했는데, 실제 운영 트래픽이 들어오기 시작합니다. 갑자기 "충분히 들어갈 것 같았던" 70B 모델을 돌리던 80GB A100에서 OOM(Out of Memory)이 발생합니다.
범인은 거의 항상 KV Cache입니다. 하지만 대부분의 논의는 "Key와 Value 행렬을 캐싱한다"는 수준에서 멈추며, 이는 언제 메모리가 부족해질지 예측하는 데 도움이 되지 않습니다.
이 포스트에서는 빠른 추정 공식(estimator formula)을 제공하고, 언제 걱정해야 하는지, 그리고 실제로 도움이 되는 조절 요소(levers)가 무엇인지 설명합니다.
하나의 공식
빠른 추정 공식은 다음과 같습니다:
KV Cache 메모리 (GB) = 2 × (layers) × (hidden_dim) × (context_length) × (bytes_per_param)
앞에 붙은 2는 K와 V를 모두 캐싱하기 때문입니다.
Llama 3.1 70B (80 layers, hidden_dim 8192, FP16)의 경우:
- 토큰당:
2 × 80 × 8192 × 2 bytes = 2.6 MB - 8K 컨텍스트(context) 시:
2.6 MB × 8192 = 21 GB - 128K 컨텍스트(context) 시:
2.6 MB × 131072 = 340 GB(A100 한 장에 들어가지 않음)
맞습니다. 128K 컨텍스트에서 70B 모델의 KV cache는 340GB의 메모리를 필요로 하며, 이는 모델 가중치(weights) 자체(FP16 기준 140GB)보다 더 큽니다.
대부분의 추론 시나리오에서 병목 현상은 가중치가 아니라 KV cache입니다.
왜 가중치보다 더 중요한가
모델 가중치는 정적(static)입니다. 한 번 로드하면 VRAM에 머물러 있습니다. FP16 기준 70B는 약 140GB입니다. 이는 이미 알고 있는 비용입니다.
KV Cache는 동적(dynamic)입니다. 다음과 같은 요소에 따라 선형적으로 증가합니다:
- 배치 크기 (Batch size) — 배치 내의 모든 시퀀스에 대해 캐싱됨
- 컨텍스트 길이 (Context length) — 모든 토큰 위치에 대해 캐싱됨
- 레이어 수 (Number of layers) — 모든 트랜스포머(transformer) 레이어(전체 스택)에 대해 캐싱됨
당신이 가장 먼저 맞닥뜨릴 벽은 다음과 같습니다:
| 시나리오 | 가중치 (Weights) | KV Cache (8K) | KV Cache (128K) |
|---|---|---|---|
| 70B, batch=1, FP16 | 140GB | 21GB | 340GB — OOM |
| ... |
긴 컨텍스트나 높은 배치 크기에서는 KV cache가 전체 메모리를 지배하며, 이는 비용을 분산(amortize)할 수 있는 부분이 아니라 트래픽과 함께 증가하는 부분입니다.
Speculative Decoding (추측적 디코딩)을 실행 중이라면 (theory, benchmarks), 초안 모델 (draft model)과 타겟 모델 (target model) 모두 각자의 KV cache를 유지합니다. 7B 초안 모델과 70B 타겟 모델 쌍의 경우, 초안 모델은 타겟 모델의 KV cache 메모리에 약 10-15%를 추가로 더하게 됩니다. 이는 추정치에 반드시 포함해야 할 요소입니다.
실제로 KV Cache 메모리를 줄이는 방법
여섯 가지 레버(levers)가 있으며, 이들은 모두 동일한 비중을 갖지는 않습니다.
레버 1: Multi-Query Attention (MQA) / Grouped Query Attention (GQA)
이것은 가장 영향력 있는 아키텍처 측면의 해결책입니다. 모든 어텐션 헤드 (attention head)에 대해 K와 V를 캐싱하는 대신, 쿼리 헤드 (query heads) 간에 K와 V를 공유합니다.
- 기존 MHA: 레이어당 KV cache =
2 × hidden_dim - GQA (8개 그룹): 레이어당 KV cache =
2 × hidden_dim / group_size(여기서group_size = num_attn_heads / kv_heads, 예: 64/8 = 8) - MQA (1개 그룹): 레이어당 KV cache =
2 × hidden_dim / num_attn_heads
실제 사례: Llama 3.1 70B는 8개의 key-value 헤드를 사용하는 GQA를 사용합니다. 이는 KV cache를 MHA가 요구하는 양의 약 1/8로 줄여줍니다 — 대략 토큰당 2.6 MB → 토큰당 0.33 MB로 감소합니다.
| 아키텍처 | 토큰당 KV (70B, FP16, 8192 hidden, 64 attn heads, head_dim=128) |
|---|---|
| MHA (64 KV heads) | 2.6 MB |
| ... |
GQA는 공짜 점심과 같습니다. 품질에는 거의 영향을 미치지 않으면서 캐시 메모리를 4-8배 절감합니다. 만약 사용 중인 모델이 이를 사용하지 않는다면, 전환을 고려해 보십시오.
레버 2: 양자화 (Quantization) (FP16 → FP8 → INT4)
KV Cache는 가중치 (weights)보다 양자화 (quantization)에 덜 민감합니다. 일반적으로 유의미한 품질 저하 없이 FP8 또는 INT4까지 진행할 수 있습니다.
| 정밀도 (Precision) | 파라미터당 바이트 (Bytes per param) | 7B, 8K, batch=16 기준 KV cache |
|---|---|---|
| FP16 | 2 | 18 GB |
| ... |
KV 캐시 양자화 (KV cache quantization)는 대부분의 추론 프레임워크 (TensorRT-LLM, vLLM, AWQ)에서 지원됩니다. KV 캐시 오류는 토큰 간에 누적되는 것이 아니라 토큰당 발생하기 때문에 품질에 미치는 영향은 미미합니다.
레버 3: 슬라이딩 윈도우 어텐션 (Sliding Window Attention)
모든 위치를 캐싱하는 대신, 마지막 N개의 토큰만 캐싱합니다. 엄격한 컨텍스트 제한 없이 ALiBi 또는 회전 위치 인코딩 (Rotary Position Encoding)을 사용하는 모델의 경우, 이를 통해 KV 캐시의 증가를 제한할 수 있습니다.
트레이드오프(Tradeoff): 모델이 윈도우 너머의 토큰에 접근할 수 없게 됩니다. 장기 의존성 (long-range dependencies)이 필요한 작업 (요약, 문서 QA)의 경우 품질이 저하됩니다.
대화형 또는 스트리밍 유스케이스의 경우, 슬라이딩 윈도우는 고민할 필요 없는 선택입니다. RAG의 경우, 관련 정보가 컨텍스트의 어느 위치에 있느냐에 따라 달라집니다.
레버 4: PagedAttention (vLLM)
vLLM의 기여는 캐시 감소가 아니라 메모리 관리입니다. 파편화 (fragmentation)를 줄여줍니다.
전통적인 추론 방식은 시퀀스당 연속적인 블록 (contiguous blocks)을 할당합니다. 만약 시퀀스의 캐시가 512 토큰이고 할당자가 1024 크기의 블록을 사용한다면, 50%가 낭비됩니다.
PagedAttention은 더 작은 페이지 (16-256 토큰) 단위로 할당하여, 파편화를 30-50%에서 1-4%로 줄입니다.
순효과: 동일한 하드웨어에서 품질 저하나 모델 변경 없이 30-50%의 유효 메모리 이득을 얻을 수 있습니다.
팀들이 vLLM으로 전환할 때 이토록 극적인 개선을 경험하는 이유가 바로 이것입니다. 연산 속도가 빨라진 것이 아니라, 메모리 패킹 (memory packing)이 더 좋아진 것입니다.
레버 5: 컨텍스트 길이 축소 (Reduce Context Length)
이는 가장 무식한(brute-force) 방법이지만, 때로는 올바른 방법이 될 수 있습니다.
| 최대 컨텍스트 (Max context) | KV cache (7B, FP16, batch=16) |
|---|---|
| 2K | 2.3 GB |
| ... |
만약 요청의 99%가 4K 토큰 미만이라면, 128K 컨텍스트를 지원하지 마세요. 사용하지 않는 컨텍스트 길이를 지원하는 것은 아무 이유 없이 VRAM을 태우는 것입니다.
vLLM과 같은 프레임워크는 요청당 컨텍스트 제한을 지원합니다. 모델의 이론적 최대치 대신 워크로드에 맞춰 max_model_len을 설정할 수 있습니다.
레버 6: 더 작은 모델 사용
때로는 가장 좋은 최적화 방법이 모델이 사용 사례에 비해 너무 크다는 사실을 인정하는 것입니다.
전체 128K 컨텍스트를 사용하는 7B 모델은 2K 컨텍스트를 사용하는 70B 모델보다 KV 캐시(KV cache) 비용이 더 많이 듭니다. 만약 작업에 긴 컨텍스트가 필요하다면, 동일한 컨텍스트 길이를 가진 대형 모델보다 더 높은 컨텍스트 길이를 지원하는 더 작은 모델이 전체 메모리를 더 적게 사용할 수 있습니다.
빠른 의사결정 트리
KV 캐시 메모리가 부족하신가요? 다음 순서대로 시도해 보세요:
1. vLLM으로 전환. 약 30-50%의 메모리 이득. 모델 변경 없음. 여기서부터 시작하세요.
2. KV 캐시를 FP8로 양자화 (Quantize). 약 2배의 메모리 감소. 품질 영향 최소화.
3. GQA 그룹 확인. 모델이 전체 MHA (Multi-Head Attention)를 사용한다면, GQA (Grouped-Query Attention) 변형 모델을 찾으세요. 4-8배 감소.
4. 슬라이딩 윈도우 (Sliding Window) 구현 또는 최대 컨텍스트 축소. 워크로드가 허용하는 경우에만 수행.
5. INT4로 양자화. FP16 대비 약 4배 감소. 먼저 데이터에 대한 품질 영향을 테스트하세요.
6. 배치 크기 (Batch size) 축소. 최후의 수단. 처리량 (Throughput)을 저하시킵니다.
빠른 추정 스크립트
def kv_cache_memory(layers, hidden_dim, context_len, batch_size, kv_heads, num_attn_heads, bytes_per_param=2):
"""
GB 단위의 KV 캐시 메모리 추정
...```
배포하기 전에 실행해 보세요. 새벽 3시에 OOM (Out of Memory) 오류를 겪는 것보다 훨씬 저렴합니다.
## 마치며
KV 캐시는 LLM 추론(Inference)에서 소리 없이 메모리를 잡아먹는 주범입니다. 모델 가중치(Weights)는 모든 관심을 받습니다. 정적이고, 눈에 보이며, 추정하기 쉽기 때문입니다. 반면 KV 캐시는 동적이며, 트래픽에 따라 증가하고, 실제 운영 환경의 배치 크기와 컨텍스트 길이에서는 종종 가중치 메모리를 초과합니다.
해결책은 단 하나의 레버(Lever)가 아닙니다. 어떤 레버를 먼저 당겨야 하는지 아는 것입니다.
메모리 관리 (vLLM)부터 시작하세요. 그다음 양자화 (FP8), 그다음 아키텍처 (GQA), 그다음 컨텍스트 제한 순서입니다. 이 순서를 지키세요. 대부분의 팀은 레버가 부족해지기 전에 문제부터 해결하게 될 것입니다.
만약 Speculative Decoding (추측적 디코딩)을 탐색 중이라면 — 이 가속 기술에는 자체적인 메모리 세금이 따릅니다. 두 모델 모두 KV 캐시를 위한 공간이 필요하기 때문입니다. 추정 시 두 모델을 모두 고려했는지 반드시 확인하세요.
**KV 캐시 (KV cache) 메모리 추정은 배포 전 체크리스트의 일부가 되어야 합니다.** 단 두 줄의 Python 코드만으로 새벽 3시에 호출될 페이지가 당신을 기다리고 있는지 확인할 수 있습니다.
*2026년 6월. 하나의 공식, 여섯 개의 레버, 하나의 결정 트리. 배포하기 전에 추정하세요 — 새벽 3시에 발생하는 OOM (Out of Memory)보다 훨씬 저렴합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기