KV 오프로딩 사용 가이드
KV 오프로딩 사용 가이드 (KV Offloading Usage Guide)
KV 캐시(KV cache)는 LLM 추론에서 큰 메모리를 차지해요. KV 오프로딩(KV Offloading) 은 완료된 KV 블록을 더 느리지만 더 큰 저장소(CPU 호스트 메모리 + 선택적으로 2차 저장소)로 오프로딩해 접두사 캐시(prefix cache)를 확장하는 기법이에요. 이 가이드는 그 동작을 담당하는 OffloadingConnector의 구성을 다룹니다.
개요 (Overview)
오프로딩 티어에 적중(hit)한 블록은 필요할 때 GPU로 다시 승격(promote) 됩니다. GPU와 CPU 사이의 전송은 DMA(cudaMemcpyAsync) 를 사용하며, 모델 연산과 비동기로 함께 실행되기 때문에 CPU/GPU 코어 오버헤드가 최소화돼요.
📌 참고:
OffloadingConnector는 현재 CUDA, ROCm, XPU만 지원합니다.
사용 가능한 스펙(spec)은 두 가지이며, kv_connector_extra_config 안의 spec_name 키로 선택해요.
CPUOffloadingSpec(기본값) — 단일 CPU 티어. 완료된 GPU 블록이 고정(pinned) 호스트 메모리에 복사됩니다.TieringOffloadingSpec(다중 티어) — CPU 1차 티어 + 하나 이상의 2차 티어(secondary tier).
전체 구조를 다이어그램으로 보면 대략 이렇습니다.
flowchart LR
GPU <--> CPU["CPU primary tier"]
CPU <--> S0["Secondary tier 0"]
CPU <--> S1["Secondary tier 1"]
CPU <--> SN["..."]
GPU → CPU(1차 티어) → 2차 티어들로 이어지는 계층 구조예요.
단일 티어 설정 (CPU 전용: Single-Tier Setup)
CPU만 쓰는 가장 단순한 구성입니다.
vllm serve <model> \
--kv-transfer-config '{
"kv_connector": "OffloadingConnector",
"kv_role": "kv_both",
...
다중 티어 설정 (Multi-Tier Setup)
spec_name을 "TieringOffloadingSpec"으로 설정하고 secondary_tiers 목록을 제공하면 돼요. 각 항목은 필수 type 키와 티어별 필드를 가진 딕셔너리예요. 목록의 순서가 중요해서, 티어 0이 티어 1보다 먼저 조회됩니다.
vllm serve <model> \
--kv-transfer-config '{
"kv_connector": "OffloadingConnector",
"kv_role": "kv_both",
"kv_connector_extra_config": {
...
}
}'
kv_connector_extra_config 레퍼런스
주요 설정 키를 정리하면 이렇습니다.
| Key | 필수 | 기본값 | 범위 | 설명 |
|---|---|---|---|---|
spec_name |
아니오 | CPUOffloadingSpec |
both | 다중 티어로 쓰려면 TieringOffloadingSpec으로 설정 |
block_size |
아니오 | GPU 블록 크기 | both | 오프로딩 블록 크기(토큰 단위). GPU 블록 크기의 배수여야 하며 blocks_per_chunk와 상호 배타적 |
blocks_per_chunk |
아니오 | 1 | both | 오프로딩 청크 크기(GPU 블록 단위). 0보다 커야 함 |
eviction_policy |
아니오 | lru | both | 1차 티어 정책: lru 또는 arc |
store_threshold |
아니오 | 0 | single-tier | 블록이 오프로딩되기 전 최소 조회 수. TieringOffloadingSpec에서는 2 이상 값이 거부됨 |
max_tracker_size |
아니오 | 64000 | single-tier | 조회 추적기의 최대 항목 수 |
secondary_tiers |
아니오 | [] | multi-tier | 2차 티어 설정 목록 |
offload_prompt_only |
아니오 | true | both | true면 프롬프트(프리필) 블록만 오프로딩하고 디코드 블록은 건너뜀 |
spec_module_path |
아니오 | — | both | 내장 레지스트리에 없는 커스텀 OffloadingSpec의 Python import 경로 |
2차 티어 (Secondary Tiers)
2차 티어는 type 키로 구분됩니다.
파일시스템 (FS)
type— 필수, 반드시fs.locality—LOCAL/REMOTE. 명시적으로 설정된 경우에만 티어의 KV 이벤트에 포함.
디스크 레이아웃: root_dir 아래에 vLLM이 <model>_<digest> 하위 디렉토리를 만들어요. <model>은 /를 _로 바꾼 모델 이름(예: meta-llama/Llama-3-8B), <digest>는 실행 구성(모델, 블록 크기 등)에서 파생된 짧은 SHA256 접두사예요.
프로세스 간 공유: 같은 root_dir(예: 공유 PVC)로 여러 vLLM 인스턴스 간 KV 캐시를 공유하려면, 모든 인스턴스에 PYTHONHASHSEED 환경 변수를 같은 고정값(예: "0")으로 설정해야 해요.
오브젝트 스토어 (OBJ)
type— 필수, 반드시obj.enable_kv_events— 성공적으로 저장된 블록에 대해BlockStoredKV 이벤트를 게시할지 여부.locality—LOCAL/REMOTE.
오브젝트 키는 파일시스템 티어와 같은 실행 구성 다이제스트 방식을 따르며, 선택적 prefix 아래에 저장돼요.
P2P (P/D 포함)
UCX 전용 에이전트로, 설정된 num_threads를 사용해요. 이 방식을 쓰면 P2P 티어가 같은 프로세스에서 실행되는 메인 NixlConnector와 다른 전송(예: MOONCAKE, GDS_MT, LIBFABRIC)을 사용할 수 있습니다.
기본값은 루프백 인터페이스에만 바인딩하므로 다른 호스트의 피어는 접근할 수 없어요. 크로스 호스트 P2P 배포라면 노드의 라우팅 가능한 IP로 명시적으로 설정해야 합니다.
튜닝 팁 (Tuning Tips)
- 단일 티어(CPU 전용):
cpu_bytes_to_use를 GPU KV 캐시 전체보다 크게 설정하세요. 오프로딩이 즉시 일어나므로, 더 작은 CPU 티어는 GPU가 이미 가진 것을 그대로 반영할 뿐 적중률을 높이지 못해요. block_size/blocks_per_chunk: 오프로딩 청크가 클수록 블록별 부기(bookkeeping) 오버헤드는 줄지만, 조회의 세분성(granularity)은 커집니다.- FS 스레드 수:
n_read_threads와n_write_threads를 저장소가 버틸 수 있는 병렬도에 맞춰 튜닝하세요.
요청별 선택적 오프로드 (Per-Request Selective Offload)
개별 요청은 kv_transfer_params의 max_offload_tokens로 오프로딩할 수 있는 토큰 수를 제한할 수 있어요. 요청의 첫 max_offload_tokens 토큰만 오프로딩되고, 그 이후 블록은 저장 경로에서 건너뜁니다.
| Key | 타입 | 설명 |
|---|---|---|
max_offload_tokens |
non-negative int | 이 요청에서 오프로딩할 토큰 수의 상한. 0이면 요청의 오프로딩을 완전히 비활성화. 키를 생략하거나 None이면 제한 없음. 정수가 아니거나 음수, bool 값은 경고와 함께 거부되고 무제한으로 처리 |
이 기능은 알려진 접두사(예: 시스템 프롬프트나 공유 컨텍스트)는 캐시할 가치가 있지만, 이후 요청별 토큰은 그렇지 않을 때 유용해요.
{
"model": "<model>",
"prompt": "...",
"kv_transfer_params": {
"max_offload_tokens": 1024
}
}
더 알아보기 (Learn more)
- 분리형 프리필 (Disaggregated Prefilling)
- 분리형 인코더 (Disaggregated Encoder)
- 자동 접두사 캐싱 (APC)
- vLLM 블로그: KV Offloading Connector — 동기, DMA 기반 비동기 전송 아키텍처, 벤치마크(TTFT·throughput)