한 줄 정의
Q8 KV는 LLM이 다음 토큰을 만들 때 다시 쓰는 KV 캐시를 8비트 계열로 저장하는 실행 설정이야. 모델 가중치를 Q8로 양자화했다는 뜻이 아니라, attention이 재사용하는 key/value 캐시를 어떤 정밀도로 들고 있을지 정하는 쪽에 가까워.
이 표현이 헷갈리는 이유는 런타임마다 이름이 다르기 때문이야. 로컬 LLM 추론 쪽에서는 q8_0 같은 정수 8비트 캐시를 Q8 KV라고 부르는 일이 있고, vLLM 문서는 공식 기능을 FP8 KV cache로 설명해. 둘 다 8비트 계열이지만 같은 구현은 아니야. 그래서 “Q8 KV를 켰다”는 말만으로는 정확한 설정을 알 수 없어.
어떻게 작동하나
Transformer 계열 모델은 이전 토큰의 key와 value를 KV 캐시에 저장해 둬. 새 토큰을 만들 때 앞 문맥을 전부 다시 계산하지 않고, 저장된 key/value를 읽어서 attention을 이어 가는 구조야. 컨텍스트가 길어질수록 캐시도 같이 커져서 GPU VRAM을 많이 먹어.
Hugging Face의 KV cache quantization 글은 Llama-2 7B를 float16으로 돌릴 때 10,000 tokens의 KV cache만 약 5GB가 필요하다는 계산을 보여줘. 여기서 dtype을 16비트에서 8비트 계열로 낮추면 원소 하나의 저장 폭은 줄어. 단순하게 보면 16비트 BF16/FP16은 2바이트, 8비트 계열은 1바이트라 캐시 압박을 줄일 수 있어.
다만 실제 절감률은 딱 절반으로 고정되지 않아. scale, zero point, residual cache, attention backend, batch size, block 관리가 같이 붙기 때문이야. Hugging Face 구현은 최근 key/value 일부를 원래 정밀도로 남기는 residual cache를 쓰고, baseline 길이를 128로 잡았어. vLLM 쪽은 FP8 KV cache에서 per-tensor scale, per-attention-head scale, calibration dataset 같은 선택지를 둬.
BF16 KV와 FP8 KV와 어떻게 다른가
BF16 KV는 캐시를 16비트 bfloat16으로 남기는 보수적인 설정이야. 긴 코딩 에이전트처럼 앞 문맥의 작은 흔들림이 뒤 function calling까지 이어지는 작업에서는 BF16 KV를 기준선으로 잡고 비교하는 게 자연스러워. 대신 같은 토큰 길이에서 Q8/FP8 캐시보다 VRAM을 더 쓴다.
FP8 KV는 vLLM 문서에 나오는 공식 경로야. fp8_e4m3, fp8_e5m2처럼 FP8 부동소수점 포맷을 쓰고, scale을 어떻게 잡느냐가 중요해. vLLM 문서는 기본 scale을 1.0으로 두는 방식, warmup 중 random token으로 scale을 잡는 방식, llm-compressor로 dataset calibration을 하는 방식을 따로 설명해. 이 셋은 같은 “FP8” 이름이어도 품질 리스크가 달라.
Q8 KV는 보통 정수 8비트 계열 캐시를 가리킬 때 많이 쓰여. Reddit 논의에서도 한 사용자가 vLLM에서 겪은 문제를 Q8 KV 문제로 생각했다가, 댓글에서 FP8과 Q8을 구분해야 한다는 지적을 받아들이는 장면이 나와. 실무에서는 이 대목을 먼저 봐야 해. 8비트라고 다 같은 8비트가 아니야.
왜 중요한가
긴 컨텍스트 작업에서는 가중치가 GPU에 올라간 뒤에도 KV 캐시가 따로 VRAM을 잡아먹어. 예를 들어 200K 안팎 문맥을 쓰는 에이전트 작업에서는 캐시 dtype 하나가 속도와 동시성, 실패 재시도 비용을 바꿔.
Reddit 스레드의 Qwen3.6 27B FP8 사용자 보고는 이 차이를 잘 보여줘. 빈 컨텍스트에서는 BF16 KV와 Q8 KV가 둘 다 약 60 tps 근처였지만, 200K context에서는 Q8 KV가 30 tps를 넘고 BF16 KV는 10 tps 아래였다고 적었어. 이 숫자는 공식 벤치마크가 아니라 특정 사용자 환경의 경험이야. 그래도 긴 문맥에서 캐시 dtype이 체감 속도를 크게 바꿀 수 있다는 신호로는 충분해.
반대로 품질 쪽은 더 조심해야 해. 같은 스레드에는 FP8 KV에서 subtle mistakes와 tool calling 문제가 보였다는 보고도 있고, f16/f16과 q8_0/q8_0가 AIME2025에서 둘 다 28/30이었다는 댓글도 있어. 결론이 하나로 안 모이는 게 정상에 가까워. 모델, backend, GPU, prompt, harness가 다르면 결과가 바뀐다.
주의해서 볼 점
첫째, Q8 KV라는 말을 보면 먼저 실제 runtime 옵션을 확인해야 해. vLLM이면 FP8 KV cache일 수 있고, 다른 로컬 runtime이면 q8_0 계열 캐시일 수 있어. 모델 이름의 Q8 가중치와 KV cache Q8도 다른 설정이야.
둘째, 짧은 벤치마크와 긴 에이전트 작업을 같은 기준으로 보면 안 돼. 단발 질문에서 괜찮아도 100K tokens를 넘는 코딩 세션에서는 초기 지시, 파일 경로, tool schema, JSON 출력 같은 작은 실수가 뒤에서 커질 수 있어.
셋째, 비교표에는 최소한 아래 항목이 같이 있어야 해.
- 모델 가중치 dtype과 KV cache dtype
- runtime과 attention backend
- 실제 prompt 길이와 max context 설정
- prefill 시간, decode tokens/sec, 최대 VRAM
- tool calling, 코드 수정, 긴 문서 검색 같은 실제 회귀 테스트
Q8 KV는 긴 컨텍스트를 더 싸게 담아 보려는 선택지야. 좋은 출발점일 수는 있지만, BF16 KV 기준선을 지우고 바로 믿을 만한 기본값으로 올리기엔 움직이는 변수가 많아.