16GB GPU에서 KV 캐시: 긴 컨텍스트를 실제로 수용하는 방법

16GB에서 128K 컨텍스트가 실패하는 이유

Page content

모델은 128K 컨텍스트 윈도우를 광고하면서도 16 GB GPU에서 40K 토큰에서 실패할 수 있습니다. 아키텍처 상한은 웨이트, KV 캐시, 컴퓨트 버퍼, 데스크톱 컴포지터가 모두 동시에 해당 카드에 들어맞겠다는 약속을 한 적이 없습니다.

KV 캐시는 보통 롱컨텍스트 계획이 물리적 한계에 부딪히는 지점입니다. 활성 토큰과 시퀀스마다 증가하므로, 시작 시 편안해 보이는 설정도 급격한 성능 저하, 시스템 메모리 스피로오버, 또는 대규모 프리필 동안의 실패로 이어질 수 있습니다.

KV cache memory budget on a 16 GB GPU

이 가이드는 이 문제를 VRAM 예산으로 전환합니다. 캐시 공식, 재현 가능한 32K에서 128K 크기 테이블, llama.cpp의 --cache-type-k--cache-type-v, vLLM의 페이징 및 프리픽스 캐싱, Ollama 컨텍스트 제어에 대한 작동 가능한 구성을 다룹니다. 또한 맹신은 금물이나 관심을 둘 가치가 있는 실험적 적응형 캐시 포크(fork)들도 포함합니다. 이 수치들 뒤의 더 폭넓은 처리량, 지연 시간, 벤치마크 맥락에 대해서는 LLM 성능 허브에서 시작하세요.

16 GB GPU를 위한 핵심 요약

단일 시퀀스, 현실적인 최대 컨텍스트, Flash Attention, 그리고 8-bit KV 캐시로 시작하세요. 4-bit 캐시, CPU 오프로드, 다중 병렬 슬롯, 또는 실험적 포크를 시도하기 전에 해당 설정을 측정하십시오.

목표 16 GB에서의 합리적인 첫 번째 시도 주요 위험
32K Q4 또는 Q5 웨이트, Q8 KV, 단일 시퀀스 모델 웨이트가 너무 적은 버퍼 공간을 남김
64K 더 작은 모델 또는 공격적인 웨이트 양자화, Q8 KV 프리필 지연 시간 및 캐시 대역폭
128K 작은 GQA 모델, Q8 또는 테스트된 Q4 KV, 단일 시퀀스 캐시만으로도 VRAM의 대부분을 차지할 수 있음
동시 64K 세션 2개 대략 128K 캐시 예산으로 취급 병렬 처리 용량을 무료 처리량으로 오인

제 의견은 간단합니다. OOM(메모리 부족) 실패의 끝자락에서 도는 명목상의 128K 설정보다 안정적인 64K 설정이 보통 더 유용합니다. 컨텍스트 용량은 트로피가 아닙니다. 이는 지연 시간, 품질, 동시성 관련 결정입니다.

KV 캐시가 저장하는 것

자기 회귀적 생성(Autoregressive generation) 동안, 각 어텐션 레이어는 처리된 각 토큰에 대해 키(Key)와 밸류(Value) 텐서를 생성합니다. 런타임은 이전 토큰을 다시 계산하지 않고 다음 토큰이 이전 토큰을 참조할 수 있도록 이러한 텐서를 유지합니다.

캐시는 상당한 컴퓨트 비용을 절감하지만, 유지되는 토큰 수에 비례하여 메모리를 소모합니다. 그룹 쿼리 어텐션(Grouped-Query Attention)을 갖춘 전통적인 트랜스포머에 대해서는 유용한 기준선이 다음과 같습니다:

KV 바이트 = 시퀀스 수 * 토큰 수 * 레이어 수 * 2 * KV 헤드 수 * 헤드 차원 * 값당 바이트 수

2라는 인자는 키와 밸류를 나타냅니다. 멀티 헤드 어텐션(Multi-head attention)은 쿼리 헤드만큼의 KV 헤드를 사용하고, 그룹 쿼리 어텐션(GQA)은 KV 헤드가 적으며, 멀티 헤드 잠재 어텐션(Multi-head latent attention) 또는 하이브리드 순환 아키텍처는 다른 계산이 필요합니다.

왜 파라미터 수만으로는 부족하지 않은가

두 개의 8B 모델은 KV 캐시 비용이 매우 다를 수 있습니다. 하나는 32개 레이어와 8개의 KV 헤드를 사용하는 반면, 다른 하나는 더 적은 KV 헤드, 공유 KV 레이어, 슬라이딩 윈도우 어텐션, 또는 압축된 잠재 상태를 사용할 수 있습니다.

파라미터 수는 주로 웨이트 메모리를 예측합니다. KV 기하 구조(Geometry)는 어텐션 아키텍처에서 나오므로, 추측 대신 모델 메타데이터를 읽어보십시오. 8B, 27B나 GGUF 파일 크기로 추정하지 마세요. 가장 명확한 예시는 어텐션 설계가 단순한 멀티 헤드 어텐션(MHA)을 넘어 어디까지 발전했는지를 보여주는 것입니다:

  • 멀티 쿼리 어텐션 (MQA): 모든 쿼리 헤드가 단일 K/V 헤드를 공유합니다 — 최대의 캐시 절감이지만, 가장 공격적인 품질 타협이며 현재 프론티어 모델에서 단독으로 거의 사용되지 않습니다.
  • 그룹 쿼리 어텐션 (GQA): 쿼리 헤드를 클러스터로 묶어 각각 하나의 K/V 헤드를 공유합니다 — 대부분의 오픈 디드슨 모델에 사용되는 주류 타협안이며, 위 공식이 전제하는 기하 구조입니다.
  • 멀티 헤드 잠재 어텐션 (MLA): DeepSeek-V2에서 도입되어 DeepSeek-V3와 Kimi K2로 이어졌습니다. 완전히 다른 접근 방식을 취합니다. 헤드를 중심으로 K/V를 공유하는 대신, 키와 밸류를 압축된 저랭크(低rank) 잠재 벡터로 투영하고, 어텐션 시점에 온디맨드로 전체 해상도의 K/V를 재구성합니다. DeepSeek은 동등한 크기의 디드슨 MHA 모델 대비 약 93%의 KV 캐시 감소를 보고했으며, 동일한 메모리 예산에서 GQA와 경쟁하거나 때로는 앞서는 품질을 유지했습니다.

실질적인 결과는 “27B GQA 모델"과 “27B MLA 모델"은 동일한 컨텍스트 길이에서 10배 차이가 나는 KV 캐시 푸트프린트를 가질 수 있다는 것입니다. 잠재 어텐션, DeltaNet 스타일의 상태, 또는 슬라이딩 윈도우 레이어를 사용한다고 스스로 문서화된 모델에 위 공식이 적용된다고 가정하지 마세요. 먼저 모델 카드의 아키텍처 섹션을 확인하십시오.

Ollama의 경우, ollama show MODEL --verbose를 사용하면 포맷이 제공하는 경우 레이어 수, 어텐션 헤드, KV 헤드, 컨텍스트 길이 등 모델 메타데이터를 확인했습니다. llama.cpp의 경우, 시작 시 출력되는 모델 로더 출력이 보통 동등한 GGUF 메타데이터와 런타임의 실제 캐시 할당을 포함합니다.

모델 한계, 할당된 컨텍스트, 사용된 컨텍스트

이것은 세 가지 별개의 숫자입니다. 모델 한계는 그 모델의 학습 및 위치 인코딩이 지원하는 최대값이고, 할당된 컨텍스트는 런타임이 예약하거나 허용하는 것이며, 사용된 컨텍스트는 현재 시퀀스를 위해 유지되고 있는 토큰입니다.

엔진 플래그를 올리면 모델을 그 모델이 지원하는 위치 스킴(위치 인코딩 방식) 이상으로 안전하게 확장할 수 없습니다. RoPE 스케일링은 일부 아키텍처를 확장할 수 있지만, 이는 모델 품질 실험이지 KV 메모리 최적화가 아닙니다.

KV 캐시 크기 테이블: 32K, 64K, 128K 컨텍스트 예산

32개 레이어, 8개 KV 헤드, 헤드 차원 128인 대표적 GQA 모델을 고려해 보겠습니다. 이러한 차원은 각 요소를 저장하는 크기로 곱하기 전에 토큰당 65,536개의 키와 밸류 요소를 생성합니다.

이 테이블은 바이너리 GiB와 llama.cpp의 f16, q8_0, q4_0과 관련되는 일반적인 물리적 블록 크기를 사용합니다. 이는 베이스라인 계산이며 총 프로세스 메모리에 대한 약속이 아닙니다; 정렬, 메타데이터, 하이브리드 레이어, 백엔드 워크스페이스가 오버헤드를 추가합니다.

캐시 유형 저장된 값당 대략 바이트 수 32K 컨텍스트 64K 컨텍스트 128K 컨텍스트
F16 2.0000 4.00 GiB 8.00 GiB 16.00 GiB
Q8_0 1.0625 2.13 GiB 4.25 GiB 8.50 GiB
Q4_0 0.5625 1.13 GiB 2.25 GiB 4.50 GiB
Q8_0 K plus Q4_0 V 혼합 1.63 GiB 3.25 GiB 6.50 GiB

이제 레이어 수를 64로 두 배로 늘리고 다른 차원은 유지해 보겠습니다. FP16 캐시는 32K에서 8 GiB, 64K에서 16 GiB, 128K에서 32 GiB가 되며, 이는 단일 컨텍스트 권장사항이 모든 모델을 커버할 수 없는 이유를 보여줍니다.

실제 16 GB 방정식

실용적인 예산은 KV 공식보다 더 광범위합니다:

사용 가능한 VRAM = 총 VRAM - 데스크톱 및 드라이버 예약분

KV 예산 = 사용 가능한 VRAM
          - GPU 상주 모델 웨이트
          - 그래프 및 활성화 버퍼
          - 런타임 워크스페이스
          - 투기적 디코딩 상태
          - 안전 마진

디스플레이에 연결된 16 GB 카드의 경우, 16 GiB 전체가 사용 가능하다고 가정하고 계획하지 마십시오. 데스크톱과 드라이버를 위해 최소한 수백 MiB를 예약한 후, 워크로드에 의존하는 버퍼를 위해 또 다른 마진을 남겨 두십시오. 총 여유 공간 1.0에서 1.5 GiB는 합리적인 시작 가정이지만, 로그가 최종 권한을 가집니다.

GGUF 모델이 GPU에서 10.8 GiB를 차지하고 런타임 오버헤드가 1.2 GiB 근처에서 피크를 치는다고 가정해 보겠습니다. 1 GiB 안전 마진 후, KV를 위해 약 3 GiB만 남습니다. 따라서 해당 대표 모델은 엔진 특정 오버헤드를 제외하고 Q8_0으로 약 45K 토큰 또는 Q4_0으로 87K 토큰을 수용할 수 있습니다.

그것이 자동으로 Q4_0이 올바른 선택이라는 것을 의미하지는 않습니다. 만약 당신의 워크로드에서 롱컨텍스트 정확도가 떨어지면, Q8_0 캐시를 가진 더 작거나 더 공격적으로 양자화된 모델이 취약한 캐시와 짝을 이룬 더 큰 웨이트보다 나을 수 있습니다. 이 산술에 대한 정확한 측정 앵커는 16 GB VRAM llama.cpp 벤치마크 테이블에 있으며, 여기서 모델별 VRAM이 19K, 32K, 64K 컨텍스트에서 기록됩니다. 동일한 클래스의 카드에서 Ollama가 잘 동작하는 모델 크기와 양자화 레벨에 대한 더 넓은 조사는 16GB VRAM GPU에서 Ollama의 LLM 성능 비교를 참고하십시오.

당신의 모델에 대한 KV 캐시 예산 계산

다음 Python 스니펫은 전통적인 풀 어텐션 GQA 캐시를 추정합니다. 모델 설정 또는 GGUF 메타데이터의 값으로 기하 구조를 교체하십시오.

def kv_gib(tokens, layers, kv_heads, head_dim, bytes_per_value, sequences=1):
    total = (
        sequences
        * tokens
        * layers
        * 2
        * kv_heads
        * head_dim
        * bytes_per_value
    )
    return total / (1024 ** 3)


model = {
    "layers": 32,
    "kv_heads": 8,
    "head_dim": 128,
}

types = {
    "f16": 2.0,
    "q8_0": 34 / 32,
    "q4_0": 18 / 32,
}

for tokens in (32768, 65536, 131072):
    row = {
        name: round(kv_gib(tokens=tokens, bytes_per_value=size, **model), 2)
        for name, size in types.items()
    }
    print(tokens, row)

Q8_0과 Q4_0의 비율은 단순한 블록 메타데이터를 포함하므로, 값당 정확히 1바이트와 0.5바이트보다 약간 더 큽니다. 런타임의 시작 리포트가 모델 특정 캐시 레이아웃을 알기 때문에 여전히 더 정확합니다.

이 공식이 틀리는 경우: 하이브리드 및 슬라이딩 윈도우 아키텍처

하이브리드 아키텍처를 전통적인 GQA 방정식에 강제로 끼워 맞추지 마십시오. 슬라이딩 윈도우 레이어는 최근 윈도우만 유지하고, 공유 KV 레이어는 중복을 줄이며, 순환 레이어는 고정 크기 상태를 가질 수 있고, 멀티 헤드 잠재 어텐션은 헤드별 K/V 텐서 대신 압축된 표현을 저장합니다 — 위 MLA 사례가 가장 극적인 예입니다.

모던 엔진은 점점 더 이러한 혼합 레이아웃을 명시적으로 관리합니다. 공식을 사용하여 지배적인 항을 설명하고, 배포할 정확한 엔진 빌드와 백엔드가 보고하는 할당을 확인하십시오.

llama.cpp: K와 V 정밀도의 직접 제어

llama.cpp는 현재 인수 파서에서 --cache-type-k--cache-type-v 옵션을 별도로 노출합니다. 이는 캐시 정밀도를 컨텍스트 용량과 트레이드오프해야 할 때 단일 글로벌 프리셋을 받아들이는 대신 가장 유용한 로컬 추론 인터페이스입니다. 먼저 주위의 설치 및 서빙 설정이 필요하다면, llama.cpp 가이드llama-cli, llama-server, 및 주요 VRAM 플래그를 다룹니다.

보수적인 64K 단일 사용자 구성은 다음과 같습니다:

./llama-server \
  --model /models/model.gguf \
  --n-gpu-layers 999 \
  --ctx-size 65536 \
  --parallel 1 \
  --flash-attn on \
  --cache-type-k q8_0 \
  --cache-type-v q8_0 \
  --batch-size 1024 \
  --ubatch-size 256

플래그 문법과 백엔드 지원은 빠르게 변하므로, 설치된 빌드에 대해 llama-server --help를 실행하십시오. 더 중요한 것은 시작 로그를 점검하는 것입니다: 의도된 컨텍스트, 캐시 유형, GPU 오프로드, 그리고 할당된 K와 V 버퍼를 보여야 합니다.

어떤 llama.cpp 캐시 유형을 시도해 볼 것인가

K와 V 모두 Q8_0으로 시작하세요. 이는 F16 대비 KV 메모리를 약 절반으로 줄이며, 20B 이상 모델(Qwen3.6-27B, Nemotron-30B)에 대한 독립적 퍼플렉시티 테스트는 F16 대비 전체 품질 차이는 측정 노이즈 범위 내에 있음을 보여줍니다 — 이는 Q4_0으로 직접 이동하는 것보다 훨씬 덜 극적인 도박이며, 동일한 테스트는 더 작은 모델에서 롱컨텍스트에서 디코딩 속도와 정확도가 붕괴하는 것을 보여주었습니다.

Q8_0이 맞지 않으면, 양쪽 모두를 Q4_0으로 양자화하기 전에 Q8_0 키와 Q4_0 밸류를 테스트하십시오. 이러한 순서는 우상학(folklore)이 아닌 연구 근거를 가지고 있습니다: Llama, Phi-4, Qwen3, Mistral 체크포인트에 대한 제어된 비트 할당 연구는 키 텐서가 밸류 텐서보다 양자화 오류에 일관되게 2에서 10배 더 민감하다는 것을 발견했으며, 키에 더 큰 비트 예산을 할당하는 것(예: 4-bit 키와 2-bit 밸류)은 풀 정밀도 정확도의 최대 94–98%를 회복할 수 있다는 것을 발견했습니다. — 반면 역전된 분할(2-bit 키, 4-bit 밸류)은 GSM8K와 같은 작업에서 30%p를 잃을 수 있습니다. 키는 어텐션이 실제로 일치하는 이전 토큰을 결정하므로, 먼저 키를 보호하는 것은 더 안전한 소리를 내는 것일 뿐만 아니라 아키텍처적으로도 타당한 선택입니다.

구성 메모리 품질 위험 권장 사항
F16 K and V 가장 높음 가장 낮음 맞으면 베이스라인
Q8_0 K and V F16의 약 절반 낮지만 제로는 아님 16 GB 기본 시작점
Q8_0 K, Q4_0 V Q8과 Q4 사이 중간 유용한 두 번째 단계
Q4_0 K and V F16의 약 1/4 가장 높음 목표 깊이를 검증

내면화할 가치가 있는 한 가지 주의사항: 전체 벤치마크에서 “낮은 품질 위험"은 토큰 레벨에서 제로 위험을 의미하지 않습니다. Flash Attention을 일정하게 유지하고 그리디(결정론적) 디코딩 하에서 KV 정밀도만 변경한 제어된 테스트는 Q8_0 캐시가 프롬프트의 대부분에서 정확한 생성 텍스트를 변경했으며, Q4_0은 사실상 모든 경우에 변경했음을 발견했습니다 — 한 번 토큰이 뒤집히면, 나머지 연속이 발산할 수 있습니다. 퍼플렉시티와 다운스트림 작업 점수는 평균적으로 잘 보일 수 있지만 개별 출력이 여전히 F16 베이스라인과 다를 수 있습니다.如果您的 애플리케이션이 바이트 단위 재현 가능성(회귀 테스트, 캐시된 응답, 결정론적 에이전트)이 필요하면, KV 양자화를 메모리 최적화뿐만 아니라 행동 변화로 취급하고, 자체 고정 프롬프트 세트에 대해 검증하십시오.

양자화된 V 캐시는 Flash Attention 또는 호환되는 백엔드 경로를 필요로 할 수 있습니다. 조용히 다른 유형으로 폴백하는 서버는 실험을 무효화하므로, 시작 로그가 복사된 명령어 라인보다 더 중요합니다.

컨텍스트, 병렬 슬롯, 통합 캐시

--ctx-size는 엔진 용량을 설명하며, 모든 병렬 슬롯이 그 많은 토큰을 독립적으로 수신한다는 보장은 아닙니다. 캐시 관리는 llama.cpp에서 발전해 왔으며, 통합 캐시 행동을 포함하므로, 컨텍스트를 슬롯 수로 단순히 나누는 오래된 규칙에 의존하기보다 정확한 빌드를 테스트하십시오.

용량 방정식은 여전히 구현 변경을 겪습니다: 동시 고유 토큰은 어딘가에 저장 공간을 필요로 합니다. 두 에이전트 세션이 각각 48K에 도달할 수 있다면, 워크로드가 프리픽스를 공유하거나 에빅션과 재계산을 용인하는 한, 약 96K의 라이브 토큰을 위한 예산을 잡으십시오.

배치 크기가 저장된 KV를 줄이지는 않음

--batch-size--ubatch-size는 프롬프트 처리와 임시 메모리에 영향을 미칩니다. 이것을 낮추면 활성화 메모리 스파이크에서 대규모 프리필을 구할 수 있지만, 각 유지되는 토큰에 필요한 영구 바이트 수는 변경하지 않습니다.

이 구별은 일반적인 실패 패턴을 설명합니다: 모델이 시작되고 빈 요청은 작동하지만, 60K 프롬프트는 수신 중 실패합니다. 트랜지언트 피크를 진단하려면 마이크로 배치를 줄이십시오; 영구 용량을 변경하려면 컨텍스트, 캐시 정밀도, 병렬성, 또는 웨이트 상주성을 줄이십시오.

vLLM: 페이징된 용량도 여전히 용량이다

vLLM은 이 문제를 서빙 엔진으로서 접근합니다. 사용 가능한 메모리를 프로파일링하고, KV 캐시 풀을 예약하며, 블록 단위로 캐시를 할당하여 동시 시퀀스가 각각 하나씩 큰 연속 영역을 요구하지 않도록 합니다. vLLM으로 이동할지 여부를 결정하고 있다면, Ollama에서 vLLM으로 마이그레이션 가이드가 워크로드 신호를 다룹니다; 여기서 질문은 단순히 풀이 얼마나 많은 캐시를 담을 수 있는지에 관한 것이며, vLLM 퀵스타트가 아래 용량 레버 외에도 설치와 일반적인 서빙 플래그를 다룹니다.

PagedAttention은 가변 시퀀스 길이 주위의 분편화와 낭비를 줄입니다 — 페이징 할당은 분편화를 제거하지만 토큰별 저장 비용을 제거하지는 않으므로, 하나의 고유한 128K 요청은 여전히 그 KV 상태를 위한 충분한 블록을 필요로 합니다.

공식 vLLM 메모리 보존 가이드는 메모리가 빠듯할 때 max_model_lenmax_num_seqs를 제한하는 것을 권장하며, CUDA 그래프가 추가 GPU 메모리를 소비한다고 명시합니다. 16 GB 카드에서, 이 두 설정은 모델의 최대 구성에서 상속되는 것이 아니라 의도적으로 설정되어야 합니다.

집중된 단일 시퀀스 서버는 여기서 시작할 수 있습니다:

vllm serve MODEL_ID \
  --max-model-len 65536 \
  --max-num-seqs 1 \
  --gpu-memory-utilization 0.90 \
  --kv-cache-dtype fp8 \
  --enable-prefix-caching

모든 16 GB GPU, 모델, 양자화 방법, 또는 어텐션 백엔드가 그 정확한 조합을 지원하지는 않습니다. 이를 구성 형상으로 취급하십시오: 길이와 동시성을 제한, 헤드룸을 예약, 지원되는 캐시 dtype을 선택하고, 초기화 리포트를 검증하십시오.

vLLM의 FP8 KV 캐시

현재 vLLM 양자화 KV 캐시 문서는 호환되는 CUDA와 ROCm 경로에서 FP8 캐시 포맷을 지원합니다. FP8은 BF16 또는 FP16 대비 원시 캐시 저장을 약 절반으로 줄이고, 따라서 토큰 용량 또는 동시성을 증가시킬 수 있습니다.

스케일링이 중요합니다. 문서는 기본 스케일, 워밍업 계산, 데이터셋 보정을 구별하며, 가장 높은 정확도를 위해 데이터셋 기반 보정을 권장합니다; 단순히 스케일 1.0으로 FP8을 설정하는 것은 편리하지만 자동으로 가장 신뢰할 수 있는 품질 선택은 아닙니다.

프리픽스 캐싱은 재사용 최적화입니다

자동 프리픽스 캐싱은 새로운 요청이 동일한 캐시된 프리픽스에 대해 KV 블록을 재사용할 수 있게 합니다. 동일한 긴 문서에 대한 반복된 쿼리, 공유 시스템 프롬프트, 다중 라운드 대화에 탁월하며, 일치하는 프리필을 재계산하지 않기 때문입니다.

이는 고유한 긴 요청을 작게 만들지 않으며, 새로운 토큰 생성을 가속하지 않습니다. vLLM 프리픽스 캐싱 문서는 이 이점을 공유 프리픽스 프리필 작업으로 명시적으로 제한합니다.

GPU 메모리 활용도는 무료 메모리가 아닙니다

--gpu-memory-utilization을 올리면 vLLM에게 더 큰 예약 목표를 제공하지만, VRAM을 생성하지는 않습니다. 1.0에 너무 가깝게 밀어 넣으면 디스플레이, 다른 프로세스, 변화하는 활성화 피크, 또는 PyTorch가 아닌 할당을 위한 공간이 부족해질 수 있습니다.

전용 16 GB GPU에서 0.88에서 0.92 근처에서 시작하고, 프로파일을 점검한 후, 워크로드가 안정적으로 유지되는 경우에만 증가시키십시오. 초기화에는 성공하지만 실제 프롬프트가 실패한다면, 할당기가 고장 난다고 가정하기 전에 배치된 토큰, 시퀀스 동시성, CUDA 그래프 캡처, 또는 최대 컨텍스트를 줄이십시오.

Ollama: 더 쉬운 제어, 더 적은 세밀한 진단

Ollama는 의도적으로 더 작은 운영 표면을 제공합니다. 현재 컨텍스트 길이 문서는 24 GiB 미만의 GPU를 기본 4K 컨텍스트로 설정하고, 에이전트 및 코딩 워크로드에는 최소 64K를 권장하며, 더 큰 컨텍스트가 더 많은 메모리를 소비한다고 경고합니다.

서버 전체 기본값을 설정하고 로드된 모델을 이렇게 확인하십시오:

OLLAMA_CONTEXT_LENGTH=65536 ollama serve

ollama ps

요청 또는 모델별로 num_ctx를 설정할 수도 있습니다. ollama ps가 중요한 이유는 PROCESSORCONTEXT 열이 모델이 GPU에 완전히 남아 있는지, 요청된 컨텍스트가 실제로 할당되었는지를 드러내기 때문입니다. Ollama 버전 사이에서 그 숫자 뒤의 스케줄링 동작이 변했음을 인지하십시오; Ollama v0.12.1 메모리 할당 비교는 새로운 스케줄러가 16 GB 카드에서 일부 모델을 CPU로 더 많이 밀어 넣는 것을 보여주므로, 측정하신 버전을 고정(pinning)하십시오.

Ollama의 양자화 KV 캐시

Ollama는 현재 FAQ에서 f16, q8_0, q4_0 선택지를 가진 OLLAMA_KV_CACHE_TYPE을 노출합니다. 양자화 KV는 Flash Attention을 필요로 하며, Ollama는 지원되는 백엔드에서 자동으로 사용하거나 OLLAMA_FLASH_ATTENTION=1로 요청할 수 있습니다.

따라서 16 GB 롱컨텍스트 서비스는 다음과 같이 시작할 수 있습니다:

OLLAMA_CONTEXT_LENGTH=65536 \
OLLAMA_FLASH_ATTENTION=1 \
OLLAMA_KV_CACHE_TYPE=q8_0 \
OLLAMA_NUM_PARALLEL=1 \
ollama serve

Q8_0은 Ollama가 F16에 대한 권장 대안으로 제시하는 것입니다. FAQ는 Q4_0이 더 주목할 만한 품질 손실을 일으킬 수 있으며, 특히 컨텍스트가 높을 때 그렇다고 경고하므로, 자동화된 16 GB 프리셋이 아니라 측정된 폴백이어야 합니다.

Ollama 병렬성이 컨텍스트 예산을 곱합니다

Ollama는 특히 명확한 규칙을 문서화합니다: 필요한 메모리는 OLLAMA_NUM_PARALLEL * OLLAMA_CONTEXT_LENGTH로 스케일링됩니다. 32K 설정에서 4개의 병렬 요청은 해당 모델에 대해 128K의 총합 컨텍스트 할당을 의미할 수 있습니다.

16 GB 개인용 에이전트에 대해, 하나의 긴 세션이 안정될 때까지 OLLAMA_NUM_PARALLEL=1을 유지하십시오. 두 번째 요청을 대기열에 넣는 것이 첫 번째 모델을 일부 CPU로 밀어 두 번째 요청까지 느리게 만드는 것보다 보통 더 좋습니다. 그 선택 뒤의 대기열, 503, 모델 언로딩 메커니즘은 Ollama가 병렬 요청을 처리하는 방법에 문서화되어 있습니다.

CPU 오프로드: 가격표가 있는 타당한 탈출구

일부 모델 레이어나 KV 상태를 시스템 RAM으로 이동하면 할당 실패를 작동하는 프로세스로 바꿀 수 있습니다. 그러나 디코딩 경로에 PCIe 대역폭과 호스트 메모리 지연 시간을 배치하며, 여기서 생성된 각 토큰이 그 비용을 지불할 수 있습니다. PCIe가 실제로 영향을 미치는 시점에 대한 레인과 생성 증거는 LLM 성능과 PCIe 레인에 있습니다.

오프로드는 가끔 발생하는 배치 작업에는 합리적이지만, 대화형 코딩 에이전트에 대한 최선의 기본값이 되는 경우는 드뭅니다. 먼저 더 작은 웨이트 양자화, Q8 KV, 감소된 동시성, 현실적인 컨텍스트 상한을 비교하십시오; 용량이 지연 시간보다 중요할 때 오프로드를 사용하십시오.

평균이 아닌 절벽(cliff)을 주시하십시오. 서버는 8K에서 빠르게 디코딩하다가 워킹 세트의 일부가 스피로오버된 후 심각하게 느려질 수 있으므로, 빈 컨텍스트 토큰 레이트만 보고하지 말고 32K, 64K, 의도된 최대값에서 벤치마크하십시오.

슬라이딩 윈도우 및 적응형 KV 캐시

슬라이딩 윈도우 어텐션은 선택된 레이어에 대해 최근 윈도우만 유지함으로써 예산을 변경합니다. 하이브리드 모델은 이러한 레이어를 가끔 글로벌 어텐션 또는 순환 상태와 결합하여, 평평한 풀 컨텍스트 계산이 메모리를 크게 과대 추정하거나 잘못 배치하게 만듭니다.

이 최적화는 일반적인 스위치가 아니라 모델 아키텍처의 일부이며, 결과 없이 적용될 수 없습니다. 엔진은 레이어 패턴, 에빅션 규칙, 위치, 그리고 모든 글로벌 토큰을 올바르게 이해해야 합니다.

적응형 KV가 개선하려는 것

실험적 포크는 레이어 및 컨텍스트 깊이를 기반으로 캐시 정밀도나 레이아웃을 선택함으로써 더 나아가려 합니다. 목표는 매력적입니다: 중요한 곳에서 더 높은 정밀도를 유지하고, 덜 민감한 레이어를 압축하며, VRAM 압력이 하드 스피로오버를 일으키기 전에 혼합을 변경합니다 — 위에서 설명된 키 민감도가 밸류 민감도보다 높은 발견은 적응형 할당기가 자동으로 활용하고 싶은 바로는 신호의 종류이며, 수동 --cache-type-k/--cache-type-v 튜닝에 맡기는 것이 아닙니다.

2026년 8월의 한 다운스트림 프로젝트인 llama.cpp-adaptive-turboquant은 여러 레이어 적응형 모드에 대한 자동 선택기를 보고하며, RTX 5080 16 GB에서 롱디프스 테스트를 공개합니다. 이러한 수치는 특수한 포크의 저자 보고된 결과이며, 업스트림 llama.cpp이 같은 방식으로 동작한다는 증거가 아닙니다.

왜 여전히 실험적인가

이 포크는 사용자 정의 캐시 유형, CUDA 커널, 모델 특정 경로, 툴체인 제약 사항을 결합합니다. 이는 업스트림 캐시 저장을 F16에서 Q8_0으로 전환하는 것보다 신뢰해야 할 코드가 훨씬 많습니다.

업스트림이 실제 요구 사항을 충족하지 못하고, 당신의 모델에서 품질, 안정성, 속도를 재현할 수 있을 때만 이러한 포크를 사용하십시오. 커밋과 CUDA 버전을 기록하십시오; 프로젝트 이름에만 첨부된 결과는 재현 불가능합니다.

공정한 적응형 캐시 테스트

포크를 동일한 GGUF, 프롬프트, 샘플러, 컨텍스트 깊이, 출력 길이와 가진 업스트림 Q8_0 베이스라인과 비교하십시오. 시작 VRAM, 피크 프리필 VRAM, 프롬프트 처리 속도, 디코딩 속도, 그리고 컨텍스트의 가장 오래된 부분에서 증거가 실제로 필요한 품질 작업을 측정하십시오.

성공적인 할당을 완전한 결과로 수용하지 마십시오. 캐시는 128K를 맞출 수 있지만 초기 사실을 잃거나, 시퀀스 후반에서 출력을 파괴하거나, 유용할 정도로 너무 느리게 디코딩할 수 있습니다.

실제 16 GB 튜닝 절차: 한 번에 한 변수씩

안정적인 구성으로의 가장 빠른 경로는 한 번에 하나의 메모리 차원을 변경하는 것입니다. 캐시 유형, 배치 크기, 레이어 오프로드, 병렬성, 컨텍스트를 무작위로 함께 변경하면 설명 없는 작동 명령을 생성합니다.

flowchart TD A["Step 1: 8K 컨텍스트, 단일 시퀀스로 로드, 워밍업 VRAM 기록"] --> B{"웨이트 + 런타임이 약 14.5 GiB 미만?"} B -- "아니오" --> C["더 작은 양자화 또는 모델 선택, Step 1 반복"] C --> A B -- "예" --> D["Step 2: F16/BF16 KV 품질 베이스라인, 작업 출력 저장"] D --> E["Step 3: Flash Attention과 함께 Q8_0 / FP8 KV로 전환"] E --> F["Step 4: 컨텍스트 단계적으로 올리기 - 32K, 64K, 96K, 128K"] F --> G{"프리필 중 실패?"} G -- "예" --> H["Step 5: 배치 / ubatch 크기 감소"] H --> F G -- "아니오" --> I{"동시 요청에서만 실패?"} I -- "예" --> J["Step 5: 병렬 슬롯 / max-num-seqs 감소"] J --> F I -- "아니오" --> K["이제야: 혼합 Q8/Q4, 전체 Q4, 오프로드, 적응형 포크"]

Step 1: 웨이트 바닥선 확립

모델을 8K 컨텍스트, 단일 시퀀스, 의도된 GPU 오프로드로 로드하십시오. 워밍업 후 프로세스 VRAM을 기록하고, 레이어가 예기치 않게 CPU로 이동하지 않았는지 확인하십시오.

웨이트와 런타임이 이미 약 14.5에서 15 GiB보다 더 많이 소비한다면, 롱컨텍스트에는 건강한 마진이 없습니다. 캐시를 튜닝하기 전에 더 작은 웨이트 양자화 또는 모델을 선택하십시오.

Step 2: F16 또는 BF16 KV를 품질 베이스라인으로 측정

테스트를 지원할 수 있는 가장 작은 컨텍스트를 실행하고, 기본 고정밀도 캐시를 유지하십시오. 검색, 코드 편집, 도구 선택, 긴 지시 작업의 출력을 저장하십시오.

이 베이스라인은 후속 오류가 캐시 양자화에서 비롯되는지 알려줍니다. 이것 없이, 챗 템플릿 문제나 약한 모델이 Q4 KV의 탓으로 돌리기 쉽습니다.

Step 3: Q8 또는 FP8로 이동

필요한 곳에서 Flash Attention을 활성화하고, llama.cpp 또는 Ollama에서 Q8_0을 선택하거나, vLLM에서 지원되는 FP8 모드를 선택하십시오. 동일한 토큰 깊이에서 동일한 프롬프트를 반복하고, 로그가 의도된 캐시 유형을 표시하는지 확인하십시오.

많은 16 GB 배포에 대해, 이것이 유용한 정지 지점입니다. 이는 스택에서 가장 공격적인 양자화로 캐시 압축을 만들지 않으면서 원시 KV 용량을 약 두 배로 늘립니다.

Step 4: 단계적으로 컨텍스트 높이기

광고된 최대값으로 직접 점프하기보다 32K, 64K, 96K, 128K를 테스트하십시오. 각 단계에서, 프롬프트 처리 초당 토큰 수, 디코딩 초당 토큰 수, 피크 VRAM, 그리고 시작 부분 근처의 증거가 여전히 회복될 수 있는지 기록하십시오.

롱컨텍스트 디코딩은 메모리가 맞아도 어텐션이 더 많은 캐시된 상태를 읽기 때문에 종종 느려집니다. 용량과 성능은 별개의 축입니다.

Step 5: 트랜지언트 메모리 튜닝

실패가 초기화 대신 프리필 중 발생하면, 마이크로 배치 또는 최대 배치된 토큰을 줄이십시오. 실패가 동시 요청에서만 발생하면, 시퀀스 동시성 또는 병렬 슬롯을 줄이십시오.

그 제어 사항이 이해된 후에야 혼합 Q8/Q4 캐시, 전체 Q4 캐시, CPU 오프로드, 또는 적응형 포크를 시도해야 합니다. 업스트림 Q8 실행을 비교 베이스라인으로 유지하십시오. 나중에 투기적 디코딩 또는 MTP를 추가한다면, 그 드래프트 버퍼가 예산 방정식의 또 다른 항목이며 무료 속도가 아님을 기억하십시오 — 투기적 디코딩 가이드가 메커니즘과 그 VRAM 비용을 다루고, 제 Qwen 3.6 27B 및 35B MTP vs 표준 벤치마크는 16 GB 카드에서 MTP 헤드의 추가 상태가 얼마나 많은 컨텍스트 비용을 요구하는지를 정확히 보여줍니다.

롱컨텍스트 벤치마크에서 기록할 것

단일 tokens/s 수치는 이 아티클이 해결하려는 정확한 문제를 은폐합니다. 롱컨텍스트 테스트는 다른 운영자가 메모리 경계를 재현할 수 있을 만큼 충분한 세부 사항을 보존해야 합니다.

필드 왜 중요한가
GPU 및 사용 가능한 VRAM 디스플레이 사용과 다른 프로세스가 예산을 변경
엔진 버전 또는 커밋 캐시 행동과 플래그가 빠르게 진화
드라이버, CUDA, ROCm, 또는 Vulkan 버전 백엔드와 커널 행동을 결정
정확한 모델과 웨이트 양자화 웨이트 상주성과 아키텍처를 정의
K와 V 캐시 유형 영구 캐시 크기와 품질 위험을 정의
컨텍스트 용량과 프롬프트 깊이 할당이 실제 깊이가 아님
병렬 시퀀스 캐시 수요를 곱하거나 공유
배치와 마이크로 배치 프리필 피크와 속도에 영향
프롬프트 처리 속도 긴 프리필 사용 가능성을 노출
각 깊이에서의 디코딩 속도 캐시 대역폭 지연을 노출
피크 VRAM과 CPU 오프로드 적합과 스피로오버를 구별
롱컨텍스트 품질 결과 압축 또는 위치 실패를 감지

프리필과 디코딩 모두 동안에 nvidia-smi 샘플링 또는 동등한 벤더 툴링을 사용하십시오. 엔진의 할당 리포트는 필수적이지만, 실제 프롬프트 동안의 피크 디바이스 메모리가 안정성을 결정하는 숫자입니다.

16 GB GPU의 일반적인 KV 캐시 실수

128K 지원을 하드웨어 약속으로 취급

모델 구성의 컨텍스트 필드는 아키텍처적 상한입니다. 특정 엔진에서 특정 양자화를 로드한 후 남은 메모리에 대해 아무것도 말하지 않습니다.

캐시를 계산하고 런타임을 검증하십시오. VRAM 예산이 없는 마케팅 크기의 컨텍스트는 단순히 첫 번째 심각한 프롬프트까지 OOM이 지연되는 것일 뿐입니다.

웨이트를 양자화하지만 KV를 잊어버림

4-bit GGUF는 모델 웨이트를 줄이지 KV F16 캐시를 줄이지 않습니다. 롱컨텍스트에서, 캐시는 전체 절감을 상쇄하고 결국 웨이트 푸트프린트를 초과할 수 있습니다.

두 양자화를 모두 보고하십시오. Q4_K_M 모델, Q8_0 KV는 의미가 있지만; 4-bit 모델은 불완전합니다.

페이징 어텐션이 토큰을 압축한다고 가정

페이징은 할당과 공유 행동을 개선합니다. 텐서 정밀도를 변경하거나, 하나의 고유한 시퀀스가 요구하는 KV 상태를 제거하지는 않습니다.

변화하는 워크로드를 효율적으로 서빙하려면 페이징 할당을 사용하십시오. 용량을 제어하려면 캐시 정밀도, 모델 아키텍처, 컨텍스트 상한, 동시성 제한을 사용하십시오.

프리픽스 캐싱이 모든 긴 프롬프트에 도움이 된다고 가정

프리픽스 캐싱은 요청이 정확한 프리픽스를 공유할 때 반복된 프리필 계산을 저장합니다. 일회성 100K 리포지토리 덤프는 프리픽스 캐싱이 활성화되어 있다고 해서 마법 같은 메모리 할인받을 수 없습니다.

이는 워크로드 최적화이며, 예산 방정식의 대용품이 아닙니다. 멀티 사용자 서빙에서 히트 레이트와 유지 캐시 압력을 측정하십시오.

품질 테스트 없이 Q4 KV 사용

저비트 캐시는 조용히 실패할 수 있습니다. 모델은 여전히 유창한 텍스트를 쓰지만, 먼 증거, 정확한 이름, 도구 인자, 또는 코드 의존성에 대한 어텐션이 열화될 수 있습니다 — 그리고 위 토큰 발산 연구가 보여주듯, “안전한” Q8_0 설정도 결정론적 디코딩 하에서 F16 출력을 정확히 재현한다고 보장되지 않으며, 단지 전체적으로 정확도를 보존할 뿐입니다.

목표 작업을 목표 깊이에서 테스트하십시오. 짧은 챗 벤치마크는 롱컨텍스트 캐시 검증을 위해 거의 무용합니다.

병렬성을 자동으로 남겨 둠

엔진이 처리량에는 합리적이지만 당신의 롱컨텍스트 목표에는 불가능한 동시성을 선택할 수 있습니다. 16 GB에서, 하나의 깊은 시퀀스와 여러 짧은 시퀀스는 근본적으로 다른 워크로드입니다.

제한을 명시적으로 설정한 후, 측정된 트래픽으로 올리십시오. 그렇지 않으면 두 번째 요청이 안정적인 64K 구성을 할당 또는 지연 시간 놀람으로 바꿀 수 있습니다.

권장 16 GB 프로필

이 프로필은 시작 위치이며 보편적 프리셋이 아닙니다. 비정상적인 KV 기하 구조를 가진 모델 — 특히 MLA 또는 하이브리드 슬라이딩 윈도우 설계 — 은 전통적인 GQA 예시보다 훨씬 저렴하거나 비쌀 수 있습니다.

대화형 코딩 에이전트

단일 시퀀스, 48K에서 64K 컨텍스트, Q8 캐시, Flash Attention, 가능하면 GPU 웨이트 전체 상주를 사용하십시오. 이 프로필은 인상적이지만 거의 유용하지 않은 최대값보다 예측 가능한 지연 시간과 좋은 캐시 정밀도를 우선시합니다.

코딩 턴이 큰 리포지토리 또는 대화 프리픽스를 공유하는 경우가 많으므로, 엔진이 지원하면 프리픽스 재사용을 활성화하십시오. 여전히 도구 출력과 오래된 기록을 압축하십시오; 캐시 엔지니어링이 무관한 토큰을 가치 있게 만들지는 않습니다.

롱문서 분석

64K에서 128K 용량, Q8 또는 보정된 FP8 캐시를 가진 더 작은 모델을 사용하고, 여러 질문이 동일한 문서를 대상으로 할 때 반복 프리픽스 캐싱을 사용하십시오. 디코딩이 여전히 수용 가능할 때에도 프리필이 지배할 수 있으므로 첫 토큰까지의 시간을 측정하십시오.

단 하나의 질문만 물어진다면, 16 GB 카드를 통해 전체 코퍼스을 통과시키는 것보다 검색이나 청크된 요약이 더 빠르고 신뢰할 수 있을 수 있습니다. 롱컨텍스트는 도구이지, 정보 아키텍처의 대용품이 아닙니다.

소규모 멀티 사용자 서버

모델 최대값을 모든 클라이언트에 광고하기보다, 요청별 컨텍스트와 총 활성 시퀀스에 상한을 설정하십시오. vLLM의 페이징 할당은 여기에서 유용하며, Ollama와 llama.cpp도 또한 총합 라이브 토큰에 대한 명시적인 주의를 요구합니다.

무제어된 스피로오버보다 대기열을 선호하십시오. 더 느린 허용(admission) 정책이 디코딩 중 모든 요청이 갑자기 PCIe를 건너뛰는 것보다 덜 해롭습니다.

16 GB 롱컨텍스트 최종 권장 사항

16 GB에서의 롱컨텍스트에 대해, Q8 KV와 하나의 활성 시퀀스가 올바른 베이스라인입니다. 이는 저비트 캐시 품질, 병렬 할당, 오프로드 지연 시간이 동시에 실패하지 않도록 실제 한계를 노출합니다.

어텐션 기하 구조에서 계산하고, 웨이트와 런타임 오버헤드를 뺀 후, 엔진 로그와 피크 메모리 측정에 결과를 확인하십시오. 128K가 여전히 맞지 않는다면, 더 작은 모델이 종종 가장 깨끗한 최적화입니다; 맞지만 거북이처럼 느린다면, 컨텍스트를 줄이는 것이 종종 정직한 선택입니다.

페이징 어텐션, 프리픽스 캐싱, 슬라이딩 윈도우, 적응형 정밀도는 모두 유용하지만 다른 문제를 해결합니다. 승리하는 구성은 GPU에 남아 있고, 오래된 증거를 올바르게 검색하며, 실제로 사용하는 컨텍스트 깊이에서 허용 가능한 디코딩 속도를 유지하는 것입니다.

참고 자료

구독하기

시스템, 인프라, AI 엔지니어링에 관한 새 글을 받아보세요.