프롬프트 캐싱, 같은 앞부분을 다시 계산하지 않기
요약
- 여러 요청이 공통으로 쓰는 앞부분의 KV 캐시를 저장해두고 재사용해, 반복되는 prefill을 건너뛰어 비용과 지연을 줄이는 기법입니다.
- KV 캐시와 다릅니다. KV 캐시는 한 번의 생성 안에서 재사용하고, 프롬프트 캐싱은 그 KV를 요청과 사용자를 가로질러 재사용합니다. 프롬프트 캐싱은 KV 캐시 위에 세워집니다.
- 핵심 규칙이 하나 있습니다. 앞에서부터 정확히 일치하는 구간까지만 유효합니다. 그래서 불변 부분을 앞에, 변하는 질문을 뒤에 둡니다.
- 입력이 길고 반복되며 출력이 짧은 워크로드에서 절감이 가장 큽니다. RAG나 문서 QA, 에이전트가 여기 해당합니다.
- prefill만 단축됩니다. 생성 자체는 그대로입니다.
무엇을 아끼나
챗봇이나 에이전트는 매 요청마다 긴 시스템 프롬프트와 문서, few-shot 예시를 다시 보냅니다. 이 앞부분은 매번 똑같은데, 캐싱이 없으면 매번 같은 수만 토큰을 처음부터 다시 prefill합니다.
프롬프트 캐싱은 여러 요청이 공유하는 동일한 앞부분의 KV를 한 번 계산해 저장하고, 다음 요청에서는 그 부분의 prefill을 생략합니다.
책갈피로 비유해봅니다. 매번 책을 처음부터 다시 읽지 않고, 공통으로 읽은 데까지 책갈피를 꽂아두고 새 질문만 이어 읽는 것입니다.
두 가지를 구분하면 개념이 또렷해집니다.
KV 캐시와의 차이. KV 캐시는 한 번의 생성 안에서 과거 토큰의 K와 V를 재사용합니다. 프롬프트 캐싱은 그 KV를 요청과 세션을 넘어 보존하고 공유합니다.
왜 앞부분만 되나. 어텐션은 인과적이라 뒤 토큰이 앞 토큰의 K와 V에 의존합니다. 그래서 앞부분이 1토큰이라도 달라지면 그 뒤는 전부 다시 계산해야 합니다. 이것이 불변 부분을 앞에 두는 이유입니다.
동작은 네 단계
1. 요청 도착 프롬프트의 앞부분이 캐시에 있는지 프리픽스 매칭으로 확인
2. 캐시 히트 일치하는 prefix는 저장된 KV를 그대로 로드 (prefill 건너뜀)
3. 캐시 미스 일치가 끝난 지점부터만 새로 prefill해 KV를 이음
4. 갱신과 만료 새 prefix를 캐시에 쓰고, TTL이나 LRU로 오래된 것 제거
밑준비로 비유해봅니다. 식당이 매 주문마다 육수를 새로 끓이지 않고, 큰 솥에 미리 끓여둔 뒤 주문만 그때그때 조리합니다.
숫자로 보면 왜 싸지나
Anthropic의 가격 배수를 정가 입력토큰 환산으로 놓고 계산해보겠습니다. 쓰기가 1.25배, 읽기가 0.1배입니다.
고정 앞부분(시스템 프롬프트와 문서) 10,000토큰에 매번 바뀌는 질문 200토큰으로 5번 질문하는 경우입니다.
| 캐싱 없음 | 캐싱 있음 | |
|---|---|---|
| 1번째 | 10,200 | 쓰기 10,000 × 1.25 + 질문 200 = 12,700 |
| 2~5번째 (각각) | 10,200 | 읽기 10,000 × 0.1 + 질문 200 = 1,200 |
| 합계 | 51,000 | 12,700 + 4 × 1,200 = 17,500 |
약 66퍼센트가 줄었습니다.
첫 요청만 약간 비싼 쓰기이고, 이후는 전부 0.1배짜리 읽기입니다. 반복이 많을수록 이득이 커지는 구조입니다. 바뀌는 질문 200토큰만 매번 정가로 내고, 고정 앞부분 10,000토큰은 한 번 쓰고 계속 싸게 읽습니다.
자동이냐 명시냐
캐싱을 켜는 방식은 두 갈래입니다.
자동은 앞부분이 겹치면 런타임이 알아서 재사용합니다. vLLM의 --enable-prefix-caching, SGLang의 RadixAttention, OpenAI 자동 캐싱이 여기 속합니다.
명시는 개발자가 캐시 경계를 지정합니다. Anthropic의 cache_control 브레이크포인트, Gemini의 명시적 context cache가 그렇습니다.
내부적으로 앞부분을 빠르게 찾는 방법도 몇 가지 있습니다.
RadixAttention은 프롬프트와 생성 KV를 기수 트리에 넣어 새 요청과 공통 접두사를 자동으로 매칭하고 재사용합니다. LRU 제거와 캐시 인지 스케줄링을 함께 씁니다.
PagedAttention은 KV를 페이지로 관리해 공유 앞부분 페이지를 여러 요청이 함께 쓰기 쉽게 만듭니다. 프리픽스 캐싱의 토대입니다.
트레이드오프
읽기는 싸고 쓰기는 약간 비쌉니다. 캐시에 쓸 때는 prefill을 해야 하므로 일반 입력보다 살짝 비싸고, 읽을 때는 대폭 할인됩니다.
메모리를 씁니다. 캐시는 GPU 메모리나 디스크를 점유하므로 TTL로 균형을 잡습니다. KV가 작을수록, 그러니까 GQA나 MLA를 쓰는 모델일수록 캐시가 쌉니다.
prefill만 단축됩니다. 생성 자체는 그대로입니다. 그래서 입력이 길고 출력이 짧은 워크로드에서 이득이 가장 큽니다.
5분과 1시간, 어느 쪽이 이득인가
Anthropic에서 1시간 TTL은 캐시 항목을 더 오래 점유시키므로 쓰기 배수가 2배입니다. 5분은 1.25배고요. 손익분기가 달라집니다.
5분 TTL 쓰기 1.25 + 읽기 0.1
2회만 재사용해도 이득 (1.25 + 0.1 < 2)
1시간 TTL 쓰기 2 + 읽기 0.2
3회 이상 재사용해야 이득 (2 + 0.2 < 3)
공백이 5분보다 길지만 재사용이 잦은 트래픽에서 1시간이 유리합니다. 사용자가 띄엄띄엄 돌아오는 패턴이면 이쪽을 봅니다.
제공자별 지원
버전과 가격은 바뀔 수 있으니 도입 시점에 공식 문서로 확인해야 합니다.
| 제공자 | 방식 | 특징 |
|---|---|---|
| Anthropic | 명시 | cache_control로 경계 지정. TTL 5분 기본, 1시간 확장. 쓰기 1.25배 또는 2배, 읽기 0.1배 |
| OpenAI | 자동 | 코드 변경 없이 1,024 토큰 이상 프롬프트에 적용 |
| Google Gemini | 명시 + 암묵 | 과금이 캐시 토큰 + 저장 시간 + 비캐시 입출력 |
| DeepSeek | 자동 | 전 사용자 기본 켜짐. 응답에 히트와 미스 토큰 수를 제공 |
| vLLM, SGLang | 자동 | --enable-prefix-caching, RadixAttention |
| llama.cpp | 자동 | 슬롯별 공통 앞부분 재사용 |
llama.cpp는 켜는 방식이 조금 다릅니다. CLI 플래그가 아니라 요청 JSON의 cache_prompt 필드이고 기본값이 true입니다. 그리고 TTL이나 과금이 아니라 내 메모리와 디스크 예산으로 캐시를 관리합니다. 온디바이스에서는 이 차이가 중요합니다.
가장 흔한 실수
변하는 내용을 프롬프트 앞에 두는 것입니다. 타임스탬프나 사용자 질문을 앞에 넣으면 앞부분이 매번 깨져 캐시가 무용지물이 됩니다.
❌ [현재 시각] [사용자 질문] [시스템 프롬프트] [문서]
└── 매번 달라짐 → 뒤가 전부 무효
✅ [시스템 프롬프트] [문서] [사용자 질문] [현재 시각]
└── 고정 ────────────┘ └── 여기만 새로 계산
캐시 히트율을 올리는 출발점은 불변 내용을 앞에 모으고 시스템 프롬프트와 few-shot을 안정적으로 유지하는 것입니다.
보안 이슈가 하나 있습니다
캐시가 사용자 간에 공유되면 첫 토큰 지연 타이밍으로 타인의 프롬프트가 누출될 수 있습니다. 캐시에 있으면 빨리 응답하고 없으면 느린데, 이 차이를 관측해 "누군가 이 프롬프트를 쓴 적이 있다"를 알아내는 사이드채널입니다.
멀티테넌트 환경에서 프롬프트 캐싱을 켤 때는 이 부분을 별도로 검토해야 합니다.
마치며
핵심 세 가지로 정리합니다.
- 프롬프트 캐싱은 요청 사이에 공통 앞부분의 KV를 재사용해 prefill을 건너뜁니다. KV 캐시 위에 세워진 층입니다.
- 앞에서부터 일치하는 구간만 유효하니 불변 부분을 앞에 둡니다. 이 한 가지가 히트율을 좌우합니다.
- 반복이 많고 입력이 긴 워크로드에서 절감이 크며, 첫 요청만 쓰기 비용을 냅니다.
용어 정리
| 용어 | 한 줄 뜻 |
|---|---|
| 프롬프트 캐싱 | 요청 간 공통 앞부분의 KV를 재사용해 비용과 지연을 줄이는 기법 |
| 프리픽스 캐싱 | 같은 뜻. 앞에서부터 일치하는 구간의 KV 재사용 |
| 캐시 히트, 미스 | 앞부분이 캐시에 있음, 없어서 새로 prefill |
| 캐시 쓰기, 읽기 | 새 앞부분을 계산해 저장, 저장된 걸 불러 씀 |
| TTL | 캐시 항목의 유효 기간 |
| RadixAttention | 기수 트리로 자동 앞부분 재사용 (SGLang) |
| 명시적, 암묵적 캐싱 | 개발자가 지정, 런타임이 자동 |
참고자료
- Anthropic, Prompt caching
- OpenAI, Prompt caching
- Google, Gemini context caching
- vLLM, Automatic prefix caching
- Kwon et al., "Efficient Memory Management for LLM Serving with PagedAttention" (SOSP 2023, arXiv:2309.06180)
- Zheng et al., "SGLang: Efficient Execution of Structured Language Model Programs" (arXiv:2312.07104)
- Gim et al., "Prompt Cache: Modular Attention Reuse for Low-Latency Inference" (MLSys 2024, arXiv:2311.04934)
댓글