메모리를 삼키는 주범 — KV Cache
← 전체 목차 · 이전: "다음 단어 예측"만으로는 부족하다 — LLM 추론 전 과정 · 다음: MoE는 일부 전문가만 활성화하는데 왜 메모리를 많이 먹나
긴 대화에서 GPU 메모리가 폭증하는 주범은 모델 가중치가 아니다. 가중치는 고정불변인 십몇 GB이고, 진짜로 팽창하는 것은 KV Cache다.
| 대상 | 행동 |
|---|
| 모델 가중치 | 고정불변, 십몇 GB |
| KV Cache | 지속 팽창, 대화가 길어질수록 |
체감 규모: 가중치는 십몇 GB인데 긴 대화에서 메모리가 수십 GB, 심지어 100 GB 이상까지 오른다.
왜 캐시가 필요한가
- 대규모 모델은 한 번에 문단 전체를 쓰지 않고 token을 하나씩 밖으로 생성한다.
- 새 token을 생성할 때마다 앞의 전체 문맥을 되돌아보며 다음에 무엇이 올지 판단한다.
- 매 단계마다 모든 과거 token을 다시 계산하면 긴 텍스트일수록 점점 느려진다(복잡도가 길이의 제곱으로 증가).
KV Cache가 하는 일
캐시 없음: t단계 → token 1..t의 K/V를 다시 계산 → 주의력 → 출력 (반복 재계산)
캐시 있음: t단계 → token t의 Q/K/V만 계산 → 캐싱된 K/V와 주의력 → 출력
└─ 새 K/V를 캐시에 추가
- 저장하는 것은 과거 token들이 이미 계산해 둔 Key와 Value다.
- 수익: 추론이 반복 재계산에서 증분 계산으로 바뀐다.
- 대가: 메모리로 연산력을 산다 — 이 문서의 가장 핵심적인 규정이다.
왜 K와 V만 캐싱하고 Q는 캐싱하지 않는가
| 벡터 | 생명주기 | 캐싱할 가치 |
|---|
| K, V | 과거 token의 K/V는 이후에도 반복적으로 조회된다 | 있음, 반드시 캐싱 |
| Q | 이번 한 번의 조회에만 봉사하고 쓰고 나면 실효 | 없음, 저장해도 메모리 낭비 |
Query의 생명주기는 현재 한 번의 조회뿐이다. 캐싱해 두면 재사용 가치 없이 메모리만 잡아먹는다.
메모리가 이렇게 빨리 느는 이유: 세 인자 분해
| 인자 | 설명 |
|---|
| 모델 층수 | 층이 많을수록, 각 층마다 저장해야 한다 (층별 독립 저장) |
| 컨텍스트 / 대화 길이 | 대화가 길수록, 새 token마다 계속 추가된다 |
| 모델 규모 | 규모가 클수록 주의력 헤드 수와 차원도 커질 수 있다 |
즉 KV Cache ≈ 층수 × 시퀀스 길이 × 헤드 수 × 헤드 차원 × 2(K와 V) × 정밀도 바이트 수.
결론: 롱컨텍스트 시나리오에서 KV Cache는 가중치보다도 메모리를 더 먹을 수 있다.
다섯 가지 엔지니어링 최적화
| 방안 | 작용점 |
|---|
| PagedAttention | 메모리 단편화 감소 |
| FlashAttention | 데이터 이동량 감소 |
| KV 양자화 | 캐시 압축 |
| 접두사 캐시(Prefix Cache) | 공통 접두사 재사용 / 중복 오버헤드 통제 |
| 슬라이딩 윈도우 | 컨텍스트 길이 통제 |
한 줄 답변
KV Cache는 추론 단계의 단기 기억이다. 생성을 빠르게 하지만, 메모리 압박을 모델 가중치에서 컨텍스트 길이로 이전시킨다.
핵심 통찰
- KV Cache는 전형적인 "시간을 공간으로 바꾸는" 조작이다 — 메모리를 희생해 연산력을 얻고, O(n²)의 반복 계산을 O(n)의 증분 계산으로 평탄화한다. "캐시"를 언급하는 모든 최적화는 첫 문장에서 무엇을 바꾸고 무엇을 지불하는지를 분명히 해야 한다.
- 어떤 중간 결과가 캐싱할 가치가 있는지는 재사용 횟수만 보면 된다 — K/V는 이후의 모든 token에 반복 조회된다(재사용 횟수 = 남은 생성 길이). Q는 한 번만 쓰인다. 이 판정 기준은 어떤 시스템의 캐시 설계에도 그대로 이식된다.
- 메모리 장부는 "정적"과 "동적" 두 권으로 나눠야 한다 — 가중치는 정적이고 예측 가능하며, KV Cache는 동적이고 요청 길이와 동시성에 따라 증가한다. 용량 계획에서 가중치만 계산하면 긴 대화에서 반드시 뒤집힌다. "모델은 십몇 GB뿐인데 메모리가 터진다"의 근본 원인이다.
- KV Cache가 롱컨텍스트의 진짜 비용 센터다 — 모델이 얼마나 긴 컨텍스트를 지원한다고 표방하는 것과, 그 길이의 KV Cache를 메모리에 실제로 담을 수 있는지는 별개다. 윈도우 길이의 상업적 가격 책정이 여기서 결정되는 경우가 많다.
- 최적화 수단은 "작용 차원"으로 분류해 기억할 것 — 단편화 감소(PagedAttention), 이동량 감소(FlashAttention), 표현 압축(KV 양자화), 시퀀스 단축(슬라이딩 윈도우), 중복 회피(접두사 캐시). 이 다섯 개의 동사만 기억하면 방안 목록을 스스로 도출할 수 있다.
- QKV의 책임 차이가 추론 단계에서 그대로 엔지니어링 결론으로 환전된다 — 학습 시점에는 "Q가 찾고, K가 찾히고, V가 내용을 준다"가 순수 이론처럼 보이지만, 추론 단계에서는 "K/V만 캐싱한다"는 강제 설계가 된다. 이론과 엔지니어링이 여기서 맞물린다.
엔지니어링 실전 Tips
- 용량 계획 공식을 종이에 적어둘 것:
메모리 ≈ 가중치 + 활성화 + 2 × 층수 × 시퀀스 길이 × KV 헤드 수 × 헤드 차원 × 바이트 수 × 동시성. 동시성이 자주 누락되며, 프로덕션 OOM의 고빈도 원인이다.
- 접두사 캐시는 다중 턴 대화와 고정 시스템 프롬프트에서 수익이 가장 크다: 같은 접두사가 수천~수만 요청 사이에서 재사용되면 중복 prefill 오버헤드를 그대로 잘라낼 수 있다.
- KV 양자화(FP8 / INT8)는 가성비가 가장 높은 한 수: KV 메모리를 거의 선형으로 압축하며 정밀도 손실은 대개 수용 가능하다. 모델 아키텍처를 바꾸는 것보다 훨씬 쉽다.
- PagedAttention이 해결하는 것은 단편화이지 총량이 아니다: 메모리를 "페이지 단위"로 할당·재사용하게 해 동시성을 높인다. 단일 요청의 KV 부피를 낮춰주리라 기대하지 말 것.
- 슬라이딩 윈도우는 손실이 있는 방안이다: 컨텍스트 길이를 통제한다는 것은 먼 과거를 능동적으로 버린다는 뜻이므로, 작업이 장거리 정보에 의존하는지 평가한 뒤 켤 것.
- 첫 글자 지연(TTFT)은 KV Cache와 강하게 연결된다: prefill 단계에서 모든 과거의 K/V를 한 번에 계산해야 하므로 긴 프롬프트는 첫 글자 지연을 직접 늘린다. "프롬프트를 너무 길게 쓰지 말라"의 엔지니어링적 근거 중 하나다(문서 08 참조).
- 더 파고들 방향: MQA / GQA(KV 헤드 수를 아키텍처 층위에서 직접 줄인다), MLA 같은 저랭크 압축 노선, Prefill과 Decode의 분리 배포(PD 분리)가 KV Cache 전송에 미치는 영향, KV Cache의 CPU/NVMe 오프로딩과 요청 간 공유.