AEGLOSLAB ChatGPT 같은 AI를 만들고 서비스하기

제5부 추론·서빙(Inference/Serving)

5.6 롱컨텍스트·프리픽스 캐시·모델 라우팅

앞선 서빙 장들은 요청 하나를 어떻게 빠르고 저렴하게 처리하느냐에 매달렸다 — KV 캐시가 왜 병목인지(5.1), 정밀도를 낮춰 메모리를 어떻게 깎는지(5.2), continuous batching과 PagedAttention으로 유휴 GPU를 어떻게 채우는지(5.3). 이 장은 한 칸 위로 올라간다. 개별 요청이 아니라 요청들 사이, 모델들 사이의 구조에서 비용을 절감한다.

올라가려면 실무 트래픽에 관한 두 사실을 인정해야 한다. 첫째, 요청들은 서로 독립이 아니다. 챗봇 100개가 같은 시스템 프롬프트로 시작하고, 계약서 하나에 스무 개 질문이 추가되고, 분류 파이프라인의 모든 호출 앞에 같은 few-shot 예시가 놓인다. 실무의 롱컨텍스트는 대부분 반복이다. 둘째, 요청들의 난이도가 제각각이다. "안녕"에 프런티어 추론 모델을 쓰는 것은 낭비고, 복잡한 다단계 추론에 소형 모델을 물리면 품질이 붕괴한다. 이 두 이질성 — 중복과 난이도 편차 — 을 이용하는 기법이 이 장의 주제다. 앞의 것은 프리픽스 캐시로, 뒤의 것은 모델 라우팅으로 공략한다.

한 가지는 미리 명시해 둔다. 이 장에는 토큰 단가 배수(캐시 읽기 0.1×·쓰기 1.25×), TTL 값, 브레이크포인트 한도, 모델별 최소 캐시 토큰 수 같은 숫자가 나온다. 이것들은 특정 시점·특정 벤더의 대표값이고 수시로 개정된다. Anthropic/Claude의 프롬프트 캐싱 구체 스펙(모델별 최소 토큰·TTL·가격·브레이크포인트 한도)은 claude-api 스킬과 공식 문서를 단일 출처로 삼아야 하고, 다른 벤더(OpenAI·Google·자체 서빙 vLLM/SGLang)도 마찬가지다. 이 장의 무게는 특정 수치가 아니라 원리와 그 이유에 있다 — 왜 캐시가 성립하는지, 어디서 붕괴하는지, 언제 이득이고 언제 불리한지.

5.6.1 길이는 두 축에서 값을 치른다

"컨텍스트 창이 1M이니 그냥 다 넣으면 된다"는 흔한 말이지만 틀렸다. 창이 크다고 넣는 것이 무비용은 아니다. 비용은 성격이 다른 두 축에서 자라고, 둘을 분리해서 봐야 최적화 지점이 보인다. 하나는 메모리, 하나는 연산이다.

5.6.1.1 메모리 — KV 캐시는 길이에 선형이다

디코더-온리 트랜스포머는 생성 중 과거 토큰의 키·값(K·V)을 캐시해 매 스텝 재계산을 피한다. 인과 마스킹(1.4) 덕에 과거 토큰의 K·V는 미래에 바뀌지 않으므로 캐시가 정당화되고, 이 캐시가 서빙의 근본 병목이라는 것을 5.1에서 산식으로 세웠다. 한 요청의 KV 캐시 크기는:

$$\text{KV bytes} = 2 \times n_{\text{tok}} \times L \times n_{\text{kv}} \times d_h \times b$$

$2$ 는 K와 V 두 텐서, $n_{\text{tok}}$ 은 시퀀스 길이(프롬프트 + 지금까지 생성한 토큰), $L$ 은 레이어 수, $n_{\text{kv}}$ 는 KV 헤드 수(MHA면 전체 헤드, GQA/MQA면 그룹 수로 줄어든다 — 1.6), $d_h$ 는 헤드 차원, $b$ 는 원소당 바이트(FP16=2, FP8=1)다. 결정적인 것은 $n_{\text{tok}}$ 에 정확히 선형이라는 것이다. 길이를 두 배로 늘리면 KV도 두 배가 된다.

숫자를 넣어 개략을 파악하자. $L=80$, $n_{\text{kv}}=8$(GQA), $d_h=128$, $b=2$ 인 대형 모델이라면 토큰 하나가 남기는 KV는

$$2 \times 80 \times 8 \times 128 \times 2 = 327{,}680 \text{ bytes} \approx 0.31\ \text{MB/토큰}$$

즉 토큰당 약 0.31 MB. 컨텍스트 32K 토큰 길이의 요청 하나는 KV로만 $32{,}768 \times 0.31\ \text{MB} \approx 10$ GB를 쓴다. 여기서 처리량이 붕괴하는 경로가 보인다. GPU HBM에서 모델 가중치를 뺀 나머지를 KV 예산이라 하고, 그것이 대략 60 GB라 하자. 32K 요청은 하나가 10 GB를 점유하므로 동시에 6개밖에 못 처리한다($60 / 10 = 6$). 반면 같은 GPU에 2K 토큰 길이의 짧은 요청이라면 하나가 $2{,}048 \times 0.31\ \text{MB} \approx 0.64$ GB이니 93개를 처리한다($60 / 0.64 \approx 93$). 컨텍스트를 16배 늘렸을 뿐인데 동시성이 15배 넘게 줄었다.

동시성이 왜 그렇게 중요한가는 5.1의 산술강도에서 나온다. 디코드는 대역폭 바운드라 배치를 크게 유지해야 GPU 활용도와 GPU당 처리량이 오르고, 그래야 토큰당 비용이 내려간다. 롱컨텍스트 요청 하나가 배치 슬롯을 크게 차지하면 배치가 좁아져 토큰당 비용이 나빠진다. GQA·MQA·MLA(1.6)로 $n_{\text{kv}}$ 를 줄이고, KV 양자화(5.2)로 $b$ 를 줄이고, 이 장 뒤의 오프로딩으로 KV를 축출하려는 시도가 전부 이 한 항 — 길이에 선형인 KV 메모리 — 을 겨냥한다.

5.6.1.2 연산 — prefill 어텐션은 길이에 제곱이다

두 번째 축은 연산량, 특히 prefill이다. prefill은 프롬프트 전체를 한 번에 순전파해 첫 토큰 직전까지 가는 단계로, TTFT(Time To First Token)를 지배한다(5.1). 길이 $n$ 프롬프트의 prefill 연산은 트랜스포머 블록의 두 부분에서 나온다.

$$\underbrace{O(n \, d^2)}_{\text{QKV·FFN 등 선형사영}} \;+\; \underbrace{O(n^2 \, d)}_{\text{어텐션 } QK^\top\text{·}AV}$$

앞 항은 길이에 선형, 뒤 항 — 어텐션의 $n\times n$ 점수 행렬 — 은 길이에 제곱이다. 어느 쪽이 지배하는지는 길이에 달렸다. 토큰 하나당으로 나눠 보면 선형 항은 $O(d^2)$, 어텐션 항은 $O(n\,d)$ 다. 어텐션이 지배하려면

$$n\,d > d^2 \quad\Longleftrightarrow\quad n > d$$

즉 프롬프트 길이 $n$ 이 모델 폭 $d$ 를 넘어서면 prefill 연산이 어텐션에 지배되기 시작한다(상수를 무시한 대략적 경계다). $d \approx 8{,}000$ 인 모델이라면 프롬프트가 8K 토큰을 넘는 순간부터 prefill은 제곱 항이 몰고 간다. 그 아래에서는 선형 항이 지배해 길이에 비례할 뿐이지만, 롱컨텍스트 영역에서는 다르다. 프롬프트를 두 배로 늘리면:

부분$n$$2n$비율
선형(FFN·사영)$c_1 n$$2 c_1 n$$\times 2$
어텐션(제곱)$c_2 n^2$$4 c_2 n^2$$\times 4$

어텐션 연산이 네 배가 된다. FlashAttention(1.4) 같은 커널 최적화는 이 제곱 연산의 메모리 대역폭·상수를 크게 줄이지만 점근 복잡도 자체는 $O(n^2)$ 로 그대로다 — 계산해야 할 곱셈의 수는 줄지 않는다. 그래서 아주 긴 프롬프트에서는 "첫 글자가 뜨기까지 한참 걸리는" 경험이 나온다. 사용자는 아무것도 못 받은 채 제곱으로 자라는 prefill이 끝나기를 기다린다.

5.6.1.3 세 가지로 비용이 크고, 그 대부분이 반복된다

두 축을 합치면 긴 프롬프트가 값을 치르는 방식이 세 갈래로 갈린다.

긴 프롬프트 (길이 n) KV 캐시 O(n) 메모리 점유 ↑ prefill 어텐션 O(n²) 연산 ↑ 배치 동시성 ↓ → 처리량 ↓ → 토큰당 비용 ↑ TTFT ↑ → 첫 응답 지연 ↑ 입력 토큰 과금 O(n) → 요청당 비용 ↑ (7.5)

① 메모리 — KV가 길이에 비례해 배치를 좁히고 토큰당 비용을 높인다. ② 연산·지연 — prefill이 길이 제곱으로 TTFT를 증가시킨다. ③ 과금 — 대부분의 API가 입력 토큰 수에 비례해 과금하니 요청당 비용이 길이에 비례해 늘어난다(7.5에서 유닛 이코노믹스로 묶는다).

그리고 여기 이 장을 여는 결정적 사실이 있다. 이 세 비용을 무는 그 긴 프롬프트가 실무에서는 대부분 반복된다. 같은 시스템 프롬프트, 같은 문서, 같은 few-shot이 요청마다 앞에 붙는다. 아주 긴 컨텍스트에서 모델이 중간 정보를 잘 못 쓰는 "lost in the middle" 현상도 있어(모델·프롬프트에 따라 다른 연구 결과다) 맹목적으로 다 넣는 것이 답도 아니다. 롱컨텍스트는 필요한 만큼만, 그리고 반복을 재사용할 수 있게 써야 한다. 그 재사용이 프리픽스 캐시다.

5.6.2 앞부분을 두 번 계산하지 마라

챗봇 요청 100개가 모두 같은 5,000토큰 길이의 시스템 프롬프트로 시작한다고 하자. 캐시가 없으면 서버는 그 5,000토큰의 prefill을 100번 반복한다 — 정확히 같은 K·V를 100번 다시 계산해서 100번 버린다. 앞 절의 세 비용을 100번 무는데, 그중 세 번째(과금)만 빼면 나머지는 순수한 낭비다. 답은 한 문장으로 나온다: 공유하는 앞부분(프리픽스)의 K·V를 한 번 계산해 저장해 두고, 같은 프리픽스로 시작하는 다음 요청은 그 저장분을 그대로 재사용한다. 그러면 두 번째 요청부터는 프리픽스의 prefill을 건너뛰고, 자기만의 고유한 뒷부분(사용자 질문)만 계산한다.

이것이 근사가 아니라 비트 동일하게 성립한다는 점이 핵심이다. 인과 어텐션에서 위치 $i$ 의 K·V는 오직 $1..i$ 까지의 토큰에만 의존한다. 그러니 프리픽스가 바이트 단위로 동일하면 그 구간의 K·V는 뒤에 무엇이 오든 완전히 같다. 우리는 어떤 근사값을 재사용하는 것이 아니라, 다시 계산해도 같을 값을 재사용한다. 이것이 프리픽스 캐시가 품질을 전혀 변경하지 않고 순수하게 비용만 깎는 이유다.

무엇이 "공유 프리픽스"가 되는가는 워크로드가 정한다. 시스템 프롬프트(페르소나·정책·도구 설명)는 세션·테넌트 전체가 공유한다. 긴 문서는 하나에 여러 질문을 하는 경우(계약서 Q&A, 코드베이스 질의) 공유된다. few-shot 예시는 분류·추출 파이프라인에서 앞에 고정된다. 대화 이력은 멀티턴에서 이전 턴 전체가 다음 턴의 프리픽스가 되므로, 매 턴이 앞 턴의 프리픽스를 재사용한다.

요청 1 요청 2 요청 3 KV 저장 KV 재사용 KV 재사용 시스템 프롬프트 + 문서 (공유 프리픽스 P) 질문 A 동일 프리픽스 P (재계산 안 함) 질문 B 동일 프리픽스 P (재계산 안 함) 질문 C 프리픽스 KV 캐시

이름이 둘인 이유도 정리해 두자. 프롬프트 캐시(prompt cache)는 매니지드 API가 노출하는 사용자 대면 기능의 이름이고(요청에 마커를 삽입하면 벤더가 알아서 캐시한다), 프리픽스 캐시(prefix cache)는 그 밑에서 실제로 KV를 재사용하는 서빙 엔진의 메커니즘 이름이다. 같은 원리의 두 층위다.

5.6.3 PagedAttention 블록 공유로 어떻게 구현하나

프리픽스 캐시가 왜 지금 사실상 표준이 됐는지는 PagedAttention(5.3)의 구조를 보면 자연스럽다. PagedAttention은 KV 캐시를 연속된 큰 텐서가 아니라 고정 크기 블록(page)들로 분할해 관리하고, 논리적 시퀀스가 어떤 물리 블록들을 쓰는지를 블록 테이블(page table)로 가리킨다 — 운영체제의 가상 메모리와 같은 발상이다. 이 구조가 프리픽스 공유를 거의 추가 비용 없이 만든다.

요청 1이 프리픽스 $P$ 의 K·V를 계산해 물리 블록 $b_1, b_2, \dots, b_k$ 에 채운다. 요청 2가 같은 $P$ 로 시작하면, 서버는 새 블록을 할당해 재계산하는 대신 요청 2의 블록 테이블이 같은 물리 블록 $b_1..b_k$ 를 가리키게 만든다. 요청 2는 자기 고유 접미부만 새 블록에 계산한다. 두 요청이 물리 KV 블록 하나를 참조 카운트로 공유하는 것이고, 이것은 운영체제의 copy-on-write와 정확히 같은 발상이다 — 누군가 그 블록을 수정하려 들면(인과성상 프리픽스 블록은 수정되지 않지만) 그때 복사하면 된다. 여기서 메모리와 연산을 동시에 아낀다. 같은 프리픽스를 한 벌만 물리적으로 보관하니 메모리가 절약되고, prefill을 한 번만 하니 연산이 절약된다.

요청 1 블록테이블 요청 2 블록테이블 b1 b2 b3 공유 프리픽스 KV (참조 카운트 2) u1 요청1 고유 u2 요청2 고유
PagedAttention 블록 공유 — 두 요청이 같은 프리픽스 블록을 참조

구현체마다 재사용의 세밀함이 다르다. vLLM의 automatic prefix caching은 블록 단위 해시로 공유 프리픽스를 자동 탐지한다. SGLang의 RadixAttention은 공유 프리픽스를 radix 트리로 관리해 완전 일치뿐 아니라 부분 일치까지 캐싱한다 — 두 요청이 앞 3,000토큰은 같고 그 뒤가 갈라지면, 트리는 공통 3,000토큰을 공유 노드로, 갈라지는 부분을 자식 노드로 두어 공통분만 재사용한다. 프레임워크별 기본값·축출 정책·블록 크기는 구현마다 다르므로 문서로 확인해야 한다. 요점은 원리다 — 블록으로 분할하면 프리픽스 공유가 포인터 조작으로 끝난다.

5.6.4 무엇을 아끼고 무엇은 못 아끼나

프리픽스 캐시가 주는 이득은 정확히 5.6.1의 세 비용에 대응한다. 세 이득을 한 표로 놓으면 대응이 선명하다.

아끼는 것원리대응 비용
prefill 연산공유 프리픽스의 $O(n_P^2)$ 어텐션을 매번 안 함 — 접미부만 계산축 2(연산·제곱)
TTFT프리픽스 prefill을 건너뛰니 첫 토큰까지 시간 급감축 2(지연)
입력 토큰 비용캐시된 프리픽스는 대폭 할인된 단가로 과금(벤더에 따라 read≈0.1×)축 3(과금)

지연 이득이 특히 극적이다. 요청 2번째부터는 프리픽스의 $O(n_P^2)$ prefill 어텐션 전체를 건너뛰므로 TTFT가 급감한다 — 사용자가 첫 글자를 훨씬 빨리 본다. 그리고 GPU가 프리픽스 재계산에 안 쓰이는 만큼 다른 요청을 더 처리할 여력이 생겨 전체 처리량도 오른다(5.3의 동시성 논리와 이어진다).

한계도 정직하게 봐야 한다. 프리픽스 캐시는 prefill(입력)만 아낀다. decode(출력 생성)는 그대로다 — 출력 토큰은 어차피 요청마다 새로 만들어야 하므로 캐시할 대상 자체가 없다. 그래서 출력이 긴 요청 — 추론 모델(3.7)이나 긴 리포트 생성 — 에서는 절감 비율이 낮다. 입력 10,000토큰에 출력 5,000토큰이면 전체 비용의 상당 부분이 캐시를 활용하지 못하는 decode 쪽이라, 프리픽스 캐시로 아무리 입력을 깎아도 총액은 크게 안 줄어든다. 이럴 땐 다른 축 — 양자화(5.2), 배칭(5.3), 또는 모델 자체를 바꾸는 이 장 뒤의 라우팅 — 을 먼저 봐야 한다.

또 캐시는 KV 저장 공간을 쓴다. 캐시를 오래·많이 보관할수록 살아 있는 요청에 쓸 메모리가 준다. 살아 있는 요청의 KV가 항상 우선이고 캐시는 남는 공간을 기회적으로 쓴다는 긴장이, 다음 절의 수명·축출 규칙, 그리고 이 장 뒤의 계층화로 이어진다.

5.6.5 적중의 유일한 규칙 — 프리픽스는 바이트 단위로 동일해야

프리픽스 캐시는 "저장했다"가 아니라 "적중(hit)했다"가 이득이다. 적중을 좌우하는 규칙은 하나뿐이고, 그것은 인과성에서 곧장 나온다. 캐시 키는 프리픽스의 정확한 토큰(바이트) 시퀀스다. 앞부분 어딘가 한 글자라도 다르면 그 지점 이후의 캐시는 전부 무효다 — 위치 $i$ 의 K·V가 앞 토큰에 의존하니, 앞이 바뀌면 뒤 K·V도 바뀌기 때문이다. 렌더링 순서도 고정이다. 대부분의 스택은 프롬프트를 도구 정의 → 시스템 프롬프트 → 메시지 순으로 조립하므로, 앞쪽(도구·시스템)이 변동하면 그 뒤 전부가 깨진다.

그래서 조심해야 할 것이 조용한 무효화 유발자(silent invalidator)다. 히트율이 0인데 에러도 안 나는 함정들이라 발견이 늦는다. 캐시를 켰다고 안심하는 사이 절감 전체가 사라진다.

유발자왜 깨지나
시스템 프롬프트에 현재 시각: {now} 삽입요청마다 프리픽스가 달라짐
프리픽스에 요청 ID·UUID·세션 ID 삽입매 요청 고유 → 절대 재사용 안 됨
JSON을 정렬 없이 직렬화(sort_keys 미사용)·집합 순회직렬화 순서가 비결정적 → 바이트가 달라짐
요청마다 도구 목록을 다르게 구성도구는 프롬프트 맨 앞 → 아무것도 캐시 안 됨
조건부 시스템 섹션(if flag: system += ...)플래그 조합마다 다른 프리픽스

계획된 무효화도 있다. 이것은 일부러 프리픽스를 바꾸는 경우인데, 결과는 같다 — 그 지점 이후가 재빌드된다. 모델 교체는 캐시가 모델별로 분리되므로 미스를 낸다(이것이 뒤의 캐스케이드 설계에서 중요해진다). 도구 집합 변경은 도구가 위치 0이니 전체 무효다. 시스템 프롬프트 내용 변경은 시스템 이후 전부를 무효화한다. 그래서 "모드 전환" 같은 것을 프리픽스를 바꿔서 구현하면 안 된다 — 도구를 교체하거나 시스템 프롬프트를 재구성하는 대신, 모드를 메시지 뒷부분에 넣거나 일부 벤더가 제공하는 대화 중 시스템 메시지 채널을 쓰는 편이 캐시를 지킨다(벤더별 지원 여부·형식은 claude-api·공식 문서 확인).

무효화가 세밀하다는 점도 알아 두면 과잉 걱정을 던다. 캐시는 도구·시스템·메시지의 세 계층으로 나뉘고, 어떤 변경은 자기 계층과 그 아래만 무효화한다. tool_choice를 바꾸거나 thinking을 켜고 끄는 정도는 도구·시스템 캐시를 무효화하지 않는다 — 그 위 계층만 살아 있으면 여전히 적중한다. 반면 도구 정의 변경과 모델 교체는 맨 위 계층을 교란해 전체를 무효화한다. 그러니 캐시 회귀를 디버깅할 때 봐야 할 것은 좁다: 프리픽스 맨 앞(도구·시스템)에서 뭐가 변동했나.

적중을 계측으로 확인해야 한다는 것이 실무의 첫 규율이다. "캐시 켰다"가 아니라 "적중하고 있다"를 봐야 한다. 대부분의 API 응답 usage에 캐시 활동이 노출된다(필드명은 벤더별로 다르다): 캐시 쓰기 토큰(이번 요청에서 캐시에 새로 쓴 양, 쓰기 프리미엄 지불), 캐시 읽기 토큰(캐시에서 읽어 대폭 할인된 양), 일반 입력 토큰(캐시 못 탄 정가 나머지). 동일 프리픽스로 반복 요청하는데 읽기 토큰이 계속 0이면, 위 표의 silent invalidator가 어딘가에서 프리픽스를 변경하고 있는 것이다 — 두 요청의 렌더된 프롬프트 바이트를 diff해 찾는다. 자체 서빙이면 프레임워크의 prefix cache hit rate 지표를 대시보드에 게시해 회귀를 감시한다(5.5의 관측성).

5.6.6 캐시는 켜는 것이 아니라 설계하는 것이다

프리픽스 캐시는 마커를 어디 찍느냐보다 프롬프트 조립 순서에서 대부분 결정된다. 모든 규칙은 한 불변식에서 나온다: 캐싱은 프리픽스 일치이고, 프리픽스 어디든 바뀌면 그 뒤 전부가 무효다. 그러니 입력을 안정성 순서로 배치한다 — 안 변하는 것을 앞으로, 자주 변하는 것을 뒤로.

프리픽스 = 캐시 대상 이 뒤는 캐시 안 함 절대 안 변함 (고정 시스템·정책·도구) 세션 단위로 변함 (긴 문서·few-shot) 요청 단위로 변함 (사용자 질문) 매 요청 변함 (타임스탬프·요청ID)

고정 시스템 프롬프트와 결정적으로 직렬화한 도구 목록을 맨 앞에 둔다. 이 대화·이 사용자에 고정된 긴 문서·few-shot을 그다음에 두면 세션 내내 재사용된다. 사용자의 이번 질문은 끝에 두고, 이것은 캐시할 대상이 아니라고 받아들인다. 타임스탬프·UUID 같은 매 요청 변동값은 프리픽스에 절대 넣지 않고 제거하거나 맨 끝으로 민다.

실무에서 이 순서를 깨는 안티패턴은 몇 가지로 정형화된다.

안티패턴문제교정
시스템 프롬프트에 날짜·사용자명 삽입프리픽스 맨 앞이 매번 달라짐날짜·이름은 메시지 뒤쪽(사용자 턴)이나 별도 시스템 메시지로
대화 중 도구 목록 스와핑으로 "모드" 구현위치 0 변경 → 전체 무효모드를 메시지 내용으로 전달, 도구 집합은 고정
포크 연산(요약·컴팩션·서브에이전트)이 시스템·도구를 재구성부모 캐시 완전 미스부모의 시스템·도구·모델을 그대로 복사하고 뒤에만 포크 내용 추가
few-shot을 요청마다 셔플프리픽스 바이트 변동예시 순서 고정

포크 연산이 특히 자주 손실되는 지점이다. 요약·컴팩션·서브에이전트가 별도 API 호출을 낼 때 시스템/도구/모델을 조금이라도 다르게 만들면 부모의 캐시 전체를 놓친다. 값싼 하위 모델이 필요한 서브태스크가 있다면 그 부분만 서브에이전트로 분리해 별도 호출로 실행하고, 메인 루프는 한 모델·한 도구집합을 끝까지 유지하는 것이 원칙이다.

매니지드 API는 어디까지를 캐시할지 표시하는 브레이크포인트(예: cache_control 마커)를 제공한다. 개념은 단순하다 — 안정 프리픽스의 마지막 블록에 마커를 삽입하면 그 지점까지가 캐시 단위가 된다(도구+시스템을 함께 캐시하려면 마지막 시스템 블록에 삽입한다). 결정적인 규칙은 공유 프리픽스가 끝나는 지점에 삽입해야 한다는 것이다. 전체 프롬프트 끝(사용자 질문 뒤)에 삽입하면 요청마다 고유해서 매번 새로 쓰고 한 번도 못 읽는다 — 쓰기 프리미엄만 부담하고 이득이 0이다. 브레이크포인트 개수 한도(예: 요청당 최대 4개)와 모델별 최소 캐시 가능 토큰 수(예: 1024~4096 토큰 미만이면 조용히 캐시 안 됨)가 있는데, 이 정확한 값은 제품·시점·모델마다 다르므로 claude-api·각 벤더 공식 문서를 단일 출처로 확인한다. 이 책이 확정으로 다루는 것은 원리 — 안정 프리픽스를 앞으로, 프리픽스 바이트를 동일하게, 변동값을 뒤로 — 뿐이다. 자체 서빙(vLLM/SGLang)이라면 automatic prefix caching·RadixAttention을 켜고 같은 설계 원칙을 적용하되 축출 정책·블록 크기는 프레임워크 문서로 확인한다.

5.6.7 수명과 축출 — 캐시는 영원히 살지 않는다

캐시된 KV는 GPU 메모리(또는 계층화된 CPU/디스크)를 점유하므로 언젠가 비워야 한다. 두 가지 수명 모델이 흔하다.

매니지드 API는 보통 TTL 기반이다. 마지막 사용 후 일정 시간이 지나면 만료되고, 대표적으로 기본 5분, 옵션으로 1시간 TTL이 쓰인다(정확한 값·갱신 규칙은 claude-api·공식 문서). TTL이 길수록 트래픽 공백을 잘 견디지만 저장 비용 — 곧 쓰기 단가 — 이 오른다. 자체 서빙(vLLM·SGLang)은 용량 기반 축출이다. KV 블록 풀이 꽉 차면 LRU(least-recently-used) 등으로 오래된/안 쓰는 프리픽스 블록을 축출한다. 살아 있는 요청의 KV가 항상 우선이고, 캐시는 남는 공간을 기회적으로 쓴다는 원칙이 여기서 강제된다.

여기서 트래픽 패턴에 따른 실무 판단이 갈린다. 버스티(bursty)한 트래픽(간헐적 폭주 사이에 긴 공백)에서는 요청 간격이 TTL보다 길면 매번 콜드 미스가 난다 — 캐시가 만료된 뒤에야 다음 요청이 오니 늘 처음부터 처리한다. 이럴 땐 1시간 TTL로 늘리거나 프리워밍(pre-warming) — 트래픽이 오기 전에 프리픽스를 미리 한 번 실행해 캐시를 채워 두는 것 — 이 유효하다. 반대로 연속 트래픽에서는 실요청이 서로 캐시를 데워 주므로 별도 프리워밍이 오히려 순수한 추가 쓰기 낭비다. 요청이 5분 안에 계속 들어오면 그들이 알아서 캐시를 살려 둔다. 그러니 "프리워밍을 켤까"의 답은 "우리 트래픽에 TTL보다 긴 공백이 있나"에 달렸다.

5.6.8 워크스루 — 프리픽스 캐시로 반복 요청 절감

식만 보면 미끄러진다. 반복 요청 시나리오를 직접 끝까지 계산해 절감폭과 손익분기를 확인하자. 단가 배수는 대표값이다(5.6 서두의 고지).

문서 하나에 여러 질문을 하는 Q&A 서비스를 놓는다. 공유 프리픽스 $P = 10{,}000$ 토큰(시스템 프롬프트 + few-shot + 긴 문서), 요청별 고유 질문 $Q = 200$ 토큰, 동일 프리픽스로 들어오는 요청 수 $N = 100$(모두 TTL 안에서). 출력은 이 예제의 절감 계산에서 제외한다 — 입력 측 prefill 절감만 보고, 출력은 어차피 캐시 대상이 아니다. 단가 배수는 정가 입력 $=1\times$, 캐시 쓰기 $=1.25\times$(5분 TTL), 캐시 읽기 $=0.1\times$ 로 둔다.

캐시 없이 매 요청이 프리픽스+질문을 정가로 prefill·과금한다.

$$N \times (P + Q)\times 1 = 100 \times (10{,}000 + 200) = 1{,}020{,}000\ \text{토큰-단가 단위}$$

프리픽스 캐시로 첫 요청과 나머지를 나눠 센다. 요청 1(콜드)은 프리픽스를 캐시에 쓰고 질문은 정가로 낸다.

$$10{,}000 \times 1.25 + 200 \times 1 = 12{,}500 + 200 = 12{,}700$$

요청 2~100(99개, 웜)은 프리픽스를 캐시에서 읽고 질문만 정가로 낸다.

$$99 \times (10{,}000 \times 0.1 + 200 \times 1) = 99 \times (1{,}000 + 200) = 99 \times 1{,}200 = 118{,}800$$

합계는 $12{,}700 + 118{,}800 = 131{,}500$ 이다.

항목캐시 없음프리픽스 캐시절감
입력 토큰-단가 단위$1{,}020{,}000$$131{,}500$$\approx 87\%$
프리픽스 prefill 횟수100회1회99%

입력 측 비용이 약 87% 줄었다. 무슨 일이 일어났나 — 10,000토큰 길이의 프리픽스를 100번 정가로 처리하던 것을, 한 번만 (조금 높은 비용으로) 쓰고 99번은 1/10 값에 읽는 것으로 바꿨다. 프리픽스가 크고($P \gg Q$) 재사용이 많을수록($N$ 큼) 절감이 커진다. 여기에 5.6.4의 지연 이득이 겹친다 — 요청 2~100은 프리픽스의 $O(P^2)$ prefill 어텐션을 건너뛰므로 TTFT가 급감하고, 그 GPU 여력이 다른 요청의 처리량으로 돌아온다.

하지만 캐시가 항상 이득은 아니다. 손익분기를 산식으로 세우면 언제 켜야 할지가 나온다. 프리픽스 $P$ 를 $N$ 번 재사용할 때(질문 $Q$ 는 어느 쪽이든 같으니 무시), 캐시 비용은 첫 쓰기 $1.25P$ 에 이후 읽기 $(N-1)\times 0.1P$ 를 더한 $P\,(1.25 + 0.1(N-1))$ 이고, 캐시 없는 비용은 $N \times P$ 다. 캐시가 이기려면

$$1.25 + 0.1(N-1) < N \;\Longleftrightarrow\; 1.15 < 0.9N \;\Longleftrightarrow\; N > 1.28$$

즉 5분 TTL은 2회 재사용부터 흑자다. 1시간 TTL은 쓰기 프리미엄이 $2\times$ 라 $2 + 0.2(N-1) < N \Leftrightarrow 1.8 < 0.8N \Leftrightarrow N > 2.25$, 즉 3회 이상이어야 흑자다. 뒤집어 말하면, 재사용이 거의 없는(프리픽스가 매번 다른) 워크로드는 캐시를 켜면 쓰기 프리미엄만 부담하고 손실이다. 프리픽스가 모델별 최소 토큰(예 1024~4096)보다 짧아도 조용히 캐시가 안 된다. 그러니 켜기 전에 물어야 할 것은 하나다 — 이 프리픽스가 실제로 재사용되는가.

5.6.9 캐시 인지 라우팅 — 같은 프리픽스를 같은 워커로

프리픽스 캐시의 이득은 캐시가 있는 GPU에 요청이 도달해야 실현된다. 그런데 서빙 인프라는 보통 여러 복제본(replica)에 걸쳐 로드밸런싱을 하고(5.5), KV 캐시는 각 GPU 로컬이다 — GPU A가 데워 둔 프리픽스는 GPU B에 없다. 프리픽스를 무시하고 라운드로빈으로 분배하면, 같은 문서에 대한 100개 질문이 8개 복제본에 흩어져 각 복제본이 프리픽스를 따로따로 콜드로 계산한다. 앞 절에서 계산한 87% 절감 전체가 사라진다.

캐시 인지 라우팅(cache-aware / prefix-aware routing)의 해법은 간단하다. 같은 프리픽스로 시작하는 요청을 (가능하면) 같은 복제본으로 보낸다. 그러면 그 복제본의 로컬 캐시가 계속 적중한다.

문서 X 질문들 → 해시 hX 문서 Y 질문들 → 해시 hY 문서 Z 질문들 → 해시 hZ 프리픽스 인지 라우터 (프리픽스 해시 → 워커) 워커 1 (문서 X 프리픽스 캐시 웜) 워커 2 (문서 Y 프리픽스 캐시 웜) 워커 3 (문서 Z 프리픽스 캐시 웜)

구현 얼개는 세 조각이다. 프리픽스 해시 어피니티(affinity) — 요청의 프리픽스(또는 세션 키·테넌트 키)를 해싱해 일관 해싱(consistent hashing)으로 워커에 매핑하면, 같은 프리픽스는 늘 같은 워커로 향한다. 부하 균형 — 순수 어피니티는 인기 프리픽스(예: 공용 시스템 프롬프트)가 한 워커에 몰려 핫스팟을 만든다. 그래서 캐시 지역성과 부하를 가중 결합한다 — "이 워커가 프리픽스를 갖고 있나(지역성)" 점수와 "이 워커가 얼마나 붐비나(부하)" 점수를 함께 보고 고른다. 세션 스티키니스 — 멀티턴 대화는 이전 턴 전체가 다음 턴의 프리픽스이므로, 한 세션을 한 워커에 고정하면 턴마다 캐시가 적중한다(5.5의 스티키 라우팅과 같은 아이디어).

트레이드오프는 어피니티의 강도에 있다. 강하게 걸수록 히트율은 오르지만 오토스케일·장애 이전(failover)이 까다로워진다 — 워커가 죽거나 스케일아웃되면 어피니티 맵이 재편되며 대량 콜드 미스가 발생한다. 그래서 지역성과 부하·가용성을 비교 평가하는 것이 라우터의 핵심 설계다. 내 판단으로는, 프리픽스가 크고 재사용이 강한 워크로드(문서 Q&A, 공용 시스템 프롬프트 챗)에서는 캐시 인지 라우팅이 프리픽스 캐시 자체만큼 중요하다 — 캐시를 켜도 라우팅이 흩뿌리면 히트율이 안 나온다. 반대로 요청이 서로 독립적이고 프리픽스 공유가 약하면 이 라우팅의 이득은 작고 단순 부하 균형이 낫다. 판단 기준은 "우리 트래픽에 공유 프리픽스가 실제로 있는가" 하나다.

5.6.10 모델 라우팅·캐스케이드 — 저렴한 모델 먼저

지금까지는 "한 모델, 여러 요청"의 캐시였다. 이제 축을 바꿔 "여러 모델, 요청별 배정"으로 간다. 서두의 두 번째 관찰이 여기서 살아난다 — 모든 요청이 최상위 모델을 필요로 하지 않는다. 난이도에 맞춰 모델을 배정하면 품질을 크게 안 잃고 비용을 줄일 수 있다. 방식은 크게 둘이다.

① 예측 라우팅 (한 번에 배정) 쉬움 어려움 요청 라우터 (난이도 예측) 소형·저가 모델 대형·고가 모델
모델 라우팅·캐스케이드(① 예측 라우팅) — 저렴한 모델 먼저(이어서 캐스케이드로)
② 캐스케이드 (순차 에스컬레이션) 예 아니오 요청 소형 모델 먼저 충분히 좋은가? (확신도·검증) 응답 채택 상위 모델로 재시도
모델 라우팅·캐스케이드(② 캐스케이드) — 저렴한 모델 먼저, 부족하면 에스컬레이션(위 그림에 이어서)

예측 라우팅(predictive routing)은 먼저 라우터가 요청의 난이도를 예측해 곧장 적절한 모델로 보낸다. 라우터는 작은 분류기·임베딩 기반 모델이거나, 규칙(길이·키워드·도구 필요 여부)이거나, 소형 LLM 자체가 "이건 상위 모델이 필요"라고 판단하는 방식이다. 장점은 요청당 추론이 한 번뿐이라는 것이고, 단점은 예측이 틀리면(쉬운 줄 알았는데 어려움) 품질 손실이 그대로 나간다는 것이다.

캐스케이드(cascade)는 저렴한 모델을 먼저 실행하고, 결과가 충분히 좋으면 채택, 아니면 상위 모델로 에스컬레이션한다. "충분히 좋은가"는 소형 모델의 자기 확신도(로그확률·자기평가), 검증기(정답 형식·단위 테스트·LLM-judge 4.2), 또는 규칙으로 판정한다. 장점은 실제로 어려운 요청만 상위 모델이 처리하니 비용이 실제 난이도에 붙는다는 것이고, 단점은 에스컬레이션되는 요청은 두 번 추론(소형 + 대형)해 지연·비용이 겹친다는 것이다.

방식요청당 추론 수비용 절감위험
항상 대형1 (대형)없음(기준)없음(품질 상한)
예측 라우팅1큼오분류 시 품질 손실이 그대로 나감
캐스케이드1~2중간~큼에스컬레이션분은 두 번 추론(지연·비용↑)

5.6.11 워크스루 — 캐스케이드의 기대비용

캐스케이드를 켤지 말지는 계산이 정해 준다. 소형 모델 비용 $c_s$, 대형 $c_\ell$, 소형이 "충분히 좋게" 처리하는 비율(수용률) $p$ 라 하면, 소형은 항상 실행되고 $1-p$ 확률로 대형이 추가로 도니 요청당 기대 비용은

$$\mathbb{E}[\text{cost}] = c_s + (1-p)\, c_\ell$$

숫자를 넣자. $c_s = 1$, $c_\ell = 20$(대형이 소형의 20배 비용이 크다), $p = 0.7$ 이면

$$\mathbb{E} = 1 + 0.3 \times 20 = 1 + 6 = 7$$

항상 대형을 쓸 때의 비용 $20$ 대비 약 35%다. 수용률이 이 곡선을 얼마나 세게 움직이는지 보자. $p$ 를 0.9로 높이면 $\mathbb{E} = 1 + 0.1\times 20 = 3$ 으로 항상-대형의 15%까지 떨어지고, 0.5로 내리면 $\mathbb{E} = 1 + 0.5\times 20 = 11$ 로 55%까지 오른다. 핵심 설계 변수는 수용 임계값이다. 임계를 높이면($p\downarrow$) 더 자주 에스컬레이션해 품질↑·비용↑, 낮추면($p\uparrow$) 비용↓·품질↓. 이 곡선 위 어디에 설지가 제품의 품질 SLA와 예산이 정한다.

손익분기도 두 방향으로 세울 수 있다. 캐스케이드가 항상-대형보다 싸려면 $c_s + (1-p)c_\ell < c_\ell$, 즉 $p > c_s / c_\ell = 1/20 = 0.05$ — 소형이 5%만 제대로 처리해도 비용상으로는 이긴다. 이 조건은 거의 항상 참이라, 비용만 보면 캐스케이드는 대개 이득이다. 하지만 이 계산은 품질을 세지 않는다. 소형이 수용한 $p$ 비율은 소형 품질을 받고, 판정기가 자신만만한 오답을 "충분히 좋다"고 통과시키면 그 품질 손실이 조용히 발생한다. 그래서 실제 손익분기는 "비용을 얼마나 아끼고 품질을 얼마나 떨어뜨리는가"의 곡선 위에서 정해지지, 비용식 하나로 끝나지 않는다.

라우터의 이득 폭도 요청 난이도 분포에 달렸다는 점을 여기서 명시해 둔다. "라우터 모델을 쓰면 항상 이득"은 과장이다. 라우팅·캐스케이드의 이득은 난이도 분포가 넓을 때 — 쉬운 것이 많을 때 — 크고, 대부분이 어렵거나 균일하면 절감이 작거나 오버헤드가 손실이다. 자기 트래픽의 난이도 분포를 먼저 재는 것이 라우팅을 논하기 전의 전제다.

5.6.12 캐스케이드의 함정 — 판정기와 캐시 상호작용

캐스케이드에서 가장 위험한 부품은 모델이 아니라 판정기다. "충분히 좋은가"를 잘못 판정하면 품질이 조용히 저하된다. 확신도만으로는 부족할 때가 많다 — 모델은 틀린 답에도 확신하기 때문이다(보정 문제). 그래서 형식 검증·규칙·외부 검증(단위 테스트·LLM-judge)을 곁들여야 판정이 믿을 만해진다. 판정기가 곧 캐스케이드의 병목이자 리스크다.

두 번째 함정은 캐시와의 상호작용이다. 5.6.5에서 봤듯 모델을 바꾸면 프리픽스 캐시가 무효다. 캐스케이드로 상위 모델에 재시도하면 그 모델에서 프리픽스를 다시 콜드로 쓴다. 캐시 이득이 큰 워크로드(공용 시스템 프롬프트·문서 Q&A)에서는 이 상호작용을 비용 모델에 반영해야 한다 — 에스컬레이션 한 번이 두 번 추론일 뿐 아니라 상위 모델 쪽 캐시 콜드까지 물기 때문이다. 이 절과 5.6.5의 프리픽스 캐시가 서로 당긴다는 것을 알아 두면 두 기법을 같이 쓸 때 비용을 잘못 추정하지 않는다.

세 번째는 라우터 자체의 비용·지연이다. 라우터가 부하가 크면 절감을 잠식한다. 라우터는 소형 분류기나 임베딩 검색처럼 저렴하고 빨라야 한다. 마지막으로 일관성이다. 요청마다 다른 모델이 답하면 응답 스타일·품질이 불균일해질 수 있다. 일관성이 최우선인 제품이라면 라우팅을 보수적으로(임계를 상위 모델 쪽으로) 걸거나 아예 쓰지 않는 편이 낫다. 라우팅 정책은 오프라인 평가(4.1·4.2)와 A/B(8.2)로 검증하고, "라우팅이 품질을 얼마나 떨어뜨리고 비용을 얼마나 아끼는가"를 수치로 관리한다 — 추측이 아니라 계측으로.

5.6.13 멀티 LoRA 서빙 — 한 베이스에 여러 어댑터

또 다른 이질성의 축이 있다. 모델 자체가 여러 변형인 경우다. 테넌트마다·태스크마다 파인튜닝한 모델을 따로 서빙하면 각 변형이 GPU 전체를 점유해 비용이 변형 수에 선형으로 급증한다. 100개 테넌트에 100개 GPU는 감당이 안 된다. 여기서 LoRA(3.8)가 서빙 최적화로 이어진다.

LoRA는 큰 베이스 가중치 $W$ 를 얼리고, 작은 저랭크 어댑터 $\Delta W = BA$($B\in\mathbb{R}^{d\times r}, A\in\mathbb{R}^{r\times k}$, $r\ll d$)만 학습한다. 순전파는 $h = Wx + BAx$ 다. 여기 서빙을 여는 사실이 있다 — $W$ 는 모든 어댑터가 공유하고, 어댑터 $BA$ 는 아주 작다(전체 파라미터의 수 %). 그러니 베이스 가중치 한 벌을 GPU에 적재해 두고, 수십~수백 개의 작은 LoRA 어댑터를 함께 메모리에 두고, 요청마다 필요한 어댑터를 동적으로 골라 적용하면 된다. 100개 GPU가 아니라 1개 베이스 + 100개 작은 어댑터를 소수 GPU에서 서빙한다. 이것이 멀티 LoRA(multi-LoRA) 서빙이다.

베이스 가중치 W (한 벌, GPU 상주) 어댑터 A₁ (테넌트1) 어댑터 A₂ (테넌트2) 어댑터 A₃ (태스크X) 이질 배치 한 배치에 서로 다른 어댑터 요청 혼합
멀티 LoRA — 베이스 가중치 공유 + 요청별 작은 어댑터 적용. 각 어댑터 = 작은 BA (수 %)

서빙의 난점은 배칭에 있다. "요청마다 어댑터를 W에 합쳐(merge) 쓰기"는 배칭을 무력화한다 — 배치 안의 요청들이 서로 다른 어댑터를 원하면 합친 가중치가 요청마다 달라 한 번의 행렬곱으로 못 묶는다. 그래서 멀티 LoRA 서빙 시스템(S-LoRA·Punica 계열 등)은 어댑터를 합치지 않고 분리 유지한다. 베이스 $Wx$ 는 배치 전체에 공통으로 한 번 계산하고, 어댑터 항 $B_i A_i x$ 는 각 요청의 어댑터로 전용 배치 커널(SGMV 등)로 계산해 더한다. 안 쓰는 어댑터는 CPU로 내리고 필요할 때 GPU로 적재하는 어댑터 스와핑·계층화도 쓴다(다음 절의 계층화와 같은 발상이다). 이렇게 서로 다른 테넌트·태스크 요청을 한 배치에 이질 혼합해 continuous batching(5.3) 위에서 GPU 활용을 유지한다.

언제 맞나는 워크로드가 정한다. 다수의 테넌트·태스크에 각각 파인튜닝이 필요하고, 각 변형의 트래픽은 GPU 하나를 꽉 채울 만큼은 아닌 경우 — "테넌트별 커스텀 모델"을 저비용으로 제공하려는 경우 — 가 적합하다. 주의할 점은 어댑터 항 계산이 무비용이 아니라 약간의 지연·처리량 오버헤드가 있고(대개 작지만 어댑터 랭크·개수가 크면 는다), 어댑터 로딩(콜드) 지연이 있어 인기 어댑터는 웜 유지해야 한다는 것이다. 어댑터의 학습·품질·라이프사이클은 3.8(PEFT)·8.3(파인튜닝 운영)의 몫이고, 여기서는 서빙 계층에서 한 베이스로 여러 변형을 저렴하게 운용한다는 것만 이해하면 된다.

5.6.14 KV 오프로딩·계층화 — 재계산과 전송을 저울질한다

프리픽스 캐시와 롱컨텍스트의 공통 압박은 GPU HBM이 좁다는 것이다. KV를 더 많이·더 오래 보관하고 싶은데 HBM은 고가이고 용량이 작다. KV 오프로딩·계층화는 운영체제의 메모리 계층처럼 KV를 속도-용량-비용이 다른 층에 나눠 둔다.

강등(demote)·축출 강등 적중 시 승격 (promote) 승격 GPU HBM 가장 빠름·가장 좁음·가장 비쌈 (활성 요청 KV) CPU DRAM 중간 속도·중간 용량 (최근/웜 프리픽스 KV) 로컬 NVMe / 원격 스토리지 느림·큼·쌈 (콜드 프리픽스 KV)

지금 디코딩 중인 요청의 KV는 어텐션이 빨라야 하니 HBM에 둔다. 방금 쓴 프리픽스 캐시는 HBM에서 축출하되 버리지 않고 CPU DRAM으로 내려 두고, 다음 적중 시 GPU로 승격(promote)한다. 오래된·큰 프리픽스는 NVMe나 오브젝트 스토리지로 강등해, 히트율은 낮지만 아주 인기 있는 공용 프리픽스(공통 시스템 프롬프트)를 재부팅·스케일아웃 뒤에도 살린다.

핵심 트레이드오프는 전송 비용 대 재계산 비용이다. KV를 CPU에서 GPU로 다시 적재하는 전송은 대역폭(PCIe/NVLink)을 점유하고 그 자체로 지연이다. 그래서 오프로딩이 이득이려면 "재계산했을 $O(n^2)$ prefill 비용" > "전송 비용"이어야 한다. 숫자로 재 보면 방향이 분명해진다. 프리픽스 $P = 10{,}000$ 토큰, 앞서의 토큰당 KV 0.31 MB라면 이 프리픽스의 KV는 $10{,}000 \times 0.31\ \text{MB} \approx 3.1$ GB다. PCIe 4.0으로 실효 25 GB/s면 CPU→GPU 전송이 $3.1 / 25 \approx 0.12$ 초. 반면 이 프리픽스를 재계산(prefill)하면 대략 $2 \times (\text{params}) \times (\text{tokens})$ FLOP이 드는데, 70B 모델이면 $2 \times 70\times10^9 \times 10{,}000 = 1.4\times10^{15}$ FLOP = 1.4 PFLOP이고, 실효 500 TFLOP/s에서 $1.4\times10^{15} / 5\times10^{14} \approx 2.8$ 초다. 전송 0.12초 대 재계산 2.8초 — 긴 프리픽스에서는 오프로딩이 20배 넘게 유리하다. 프리픽스가 길수록 재계산이 비용이 커 오프로딩이 이기고, 짧으면 전송 오버헤드가 아까워 단순히 다시 계산하는 것이 낫다. 여기에 KV 양자화(5.2)를 곁들이면 전송·저장할 바이트를 줄여 계층화 효율이 오른다.

이 계산은 prefill-decode 분리(disaggregation)와도 이어진다 — 일부 시스템은 prefill과 decode를 다른 GPU 풀에서 실행하고 KV를 그 사이로 전송하는데, 이때 KV 전송 경로가 곧 계층화의 일부가 된다. 다만 계층화·오프로딩은 프레임워크·하드웨어(NVLink 유무·NVMe 속도)에 강하게 의존하고 설정이 까다롭다. "무조건 켜면 이득"이 아니라 워크로드(프리픽스 길이·재사용 패턴)와 하드웨어에 맞춰 측정해야 한다 — 위 산식은 방향을 주고, 실측이 값을 준다.

이 장의 기법은 전부 같은 관찰 위에 서 있다. 요청들은 서로 독립이 아니고 난이도도 제각각이라는 것. 프리픽스 캐시는 그 중복을 재사용해 반복의 prefill과 TTFT를 지우고, 캐시 인지 라우팅은 그 재사용을 실현할 GPU로 요청을 보내고, 모델 라우팅·캐스케이드는 난이도에 모델을 맞추고, 멀티 LoRA는 한 베이스로 여러 변형을 저렴하게 운용하고, KV 계층화는 재계산이 전송보다 비용이 클 때 KV를 축출한다. 어느 것도 무조건 이득이 아니라, 자기 워크로드의 측정 — 프리픽스 공유율, 재사용 간격, 난이도 분포, 프리픽스 길이 — 이 켤지 말지를 정한다. 여기서 절감한 비용을 유닛 이코노믹스로 묶어 마진·가격으로 환산하는 것이 다음 부의 7.5이다.