AI 연구EN

KV 캐시 줄이는 12가지, A100 한 장에서 실측 — 1편: 12개를 한 줄로 세울 수 없습니다

KV 캐시 기법 목록은 12개를 나란히 놓지만 각각이 얼마인지는 말하지 않습니다. A100 한 장에서 재보니 프리픽스 재사용은 공유 프리픽스 작업을 59.4초에서 6.2초로 줄였고, paged 블록 크기는 용량을 1%도 바꾸지 못했습니다. 둘을 한 줄로 세울 수 없는 이유는 지불 단위가 다르기 때문입니다.

KV 캐시 줄이는 12가지, A100 한 장에서 실측 — 1편: 12개를 한 줄로 세울 수 없습니다

KV 캐시 줄이는 12가지, A100 한 장에서 실측 — 1편: 12개를 한 줄로 세울 수 없습니다

KV 캐시를 줄이는 기법 12가지를 정리한 글이 자주 돌아다닙니다. 그중 잘 쓴 것들은 마지막에 이런 단서를 답니다. 이 기법들이 전부 캐시를 압축하는 것은 아니고, 어떤 것은 저장량을 줄이고 어떤 것은 읽기량이나 중복 계산을 줄이며 어떤 것은 바이트가 놓이는 위치만 바꾼다고요.

그 단서는 맞습니다. 그리고 대개 거기서 글이 끝납니다. 12개를 나란히 세워 두고, 각각이 얼마짜리인지는 말하지 않습니다. 그래서 A100 한 장에 올려 직접 쟀습니다.

결론은 순위표가 아닙니다. 이 질문에는 순위표라는 형식 자체가 맞지 않습니다. 12개 중 둘은 어떤 척도로 재도 양 끝에 놓였는데, 하나는 작업 시간을 59.4초에서 6.2초로 줄였고 다른 하나는 용량을 1%도 못 바꿨습니다. 그런데 이 둘은 애초에 같은 단위로 적힌 값이 아닙니다. 이번 편은 2편의 숫자가 뜻을 갖도록 측정 기반을 세우는 자리이고, 조합했을 때 무너지는 것은 3편에서 다룹니다.

무엇을 어디서 쟀는지

A100 80GB PCIe 한 장, 드라이버 535.288.01, CUDA 12.2, vLLM 0.28.0, bf16 가중치의 Qwen3-8B, max_model_len 40,960, gpu_memory_utilization 0.90입니다.

아래 숫자는 모두 토큰당 바이트에 묶어 둡니다. 아키텍처 쪽 기법들이 공격하는 대상이 바로 이 값입니다. Qwen3-8B의 모델 설정을 보면 레이어 36개, KV 헤드 8개, 헤드 차원 128, 키와 값 각각 2바이트입니다.

36 × 8 × 128 × 2 × 2 = 147,456바이트 = 토큰당 144KB

이 비율에서 엔진이 보고한 GPU KV 캐시는 400,032토큰이고, 40,960토큰 요청의 최대 동시 처리는 9.77개였습니다. 산수가 정확히 닫힙니다. 400,032 × 147,456바이트는 54.94 GiB이고, 이 값이 vLLM이 사용 가능 KV 캐시 메모리로 출력한 숫자와 같습니다. 이 계산은 한 번 손으로 해 보시는 편이 좋습니다. 이후 편에 나올 용량 이야기를 그대로 믿는 대신 직접 검산하실 수 있기 때문입니다.

이 리그는 제가 이미 발행한 두 글과 같습니다. TurboQuant vLLM 실측하이브리드 Mamba 실측입니다. 두 글의 bf16 기준선은 396,192토큰(동시 9.7개)과 약 40만 토큰(동시 9.8개)이었고, 오늘 나온 400,032와 9.77은 양쪽 모두와 1% 안에서 만납니다. 두 글의 숫자를 새 측정과 한 표에 올릴 수 있는 것은 이 일치 덕분입니다.

이 기법들이 실제로 하는 일은 네 가지입니다

이름 대신 작동 방식으로 12개를 묶으면 네 갈래가 나옵니다.

저장하는 바이트를 줄입니다. GQA와 MQA, 레이어 간 KV 공유, multi-head latent attention, 슬라이딩 윈도 어텐션, 하이브리드 Mamba 레이어, KV 양자화, 토큰 축출, 압축 어텐션. 12개 중 여덟입니다.

중복 계산을 줄입니다. 프리픽스 재사용. 하나입니다.

읽기를 줄입니다. 쿼리 기반 희소 읽기. 하나입니다.

바이트는 그대로 두고 놓이는 위치나 배치만 바꿉니다. paged 할당, 오프로딩. 둘입니다.

여기서 첫 번째로 눈에 걸리는 것은, 목록이 보이는 만큼 다양하지 않다는 점입니다. 12개 중 3분의 2가 한 가지 일을 합니다. 토큰 하나를 저장하는 값을 싸게 만드는 것입니다. 방법은 KV 헤드를 줄이거나, 상태를 들고 있는 레이어를 줄이거나, 값 하나의 비트 수를 줄이거나, 남겨 두는 토큰 수를 줄이는 것으로 갈리지만, 결국 같은 곱셈식의 같은 항을 건드립니다. 그래서 둘을 겹쳐 쓰면 더해지는 것이 아니라 곱해집니다.

두 번째로 걸리는 것이 이 시리즈를 시작한 이유입니다. 항목이 하나뿐인 갈래에서 제가 측정한 가장 큰 숫자가 나왔습니다.

목록의 순서를 바꾼 두 측정

프리픽스 재사용: 프리픽스에 따라 1.0배에서 9.6배까지

워크로드는 에이전트나 RAG 서비스에서 실제로 나오는 형태로 만들었습니다. 요청 32개가 긴 프리픽스를 공유하고 짧은 접미사에서만 갈립니다. 요청마다 고유한 꼬리가 256토큰이고 출력은 64토큰입니다. 모든 요청이 처음 들어온 것이라, 같은 질문을 두 번 보내서 얻는 이득이 아니라 한 배치 안에서 생기는 재사용을 재고 있습니다.

공유 프리픽스캐싱 끔캐싱 켬배수
0토큰1.378초1.389초1.00배
1,0243.916초1.647초2.38배
4,09612.484초2.512초4.97배
8,19225.900초3.652초7.09배
16,38459.386초6.199초9.58배
공유 프리픽스 길이 0, 1K, 4K, 8K, 16K에서 요청 32개의 처리 시간을 프리픽스 캐싱 끔과 켬으로 묶어 비교한 막대 그래프입니다. 공유가 0토큰일 때 두 막대는 1.4초로 같고, 16K에서 59.4초와 6.2초로 9.58배까지 벌어집니다.

첫 줄이 대조군이고, 나머지를 믿는 근거가 이 한 줄입니다. 공유할 것이 없으면 재사용할 것도 없으니 두 설정이 같은 값에 놓여야 하는데, 실제로 0.8% 차이로 붙었습니다. 그 아래 숫자들은 워밍업이나 측정 흔들림이 아니라 기법이 한 일입니다.

캐싱을 끄면 프리필 처리량이 공유 길이와 무관하게 초당 8,967토큰에서 11,156토큰 사이에 머물렀습니다. 실제로 계산을 하면 이렇게 나옵니다. 켜면 초당 5,898토큰에서 85,903토큰까지 올라갑니다. 이 값은 GPU가 빨라진 것이 아닙니다. GPU가 처리하지 않은 토큰까지 분자에 세고 있는 것입니다.

여기서 기억해 둘 것은 이 배수가 무엇에 따라 달라지는지입니다. 요청에서 공유되는 몫이 얼마나 되는지가 배수를 정합니다. 전체 16.6K 중 16K가 공유일 때 9.6배이고, 공유가 0이면 정확히 아무것도 아닙니다. 프리픽스 캐싱의 효과를 숫자 하나로 말하는 사람은 자기 워크로드를 말하고 있는 것입니다.

paged 할당: 블록 크기를 바꿔도 얻는 것이 없습니다

block_sizeGPU 블록 수KV 토큰40K 요청 동시
16 (기본)25,002400,0329.77
3212,381396,1929.67
646,190396,1609.67

전 구간이 1% 안입니다. 이것은 paged 할당을 깎아내리는 이야기가 아닙니다. 애초에 단편화가 캐시의 3분의 1을 먹고 있지 않은 이유가 paged 할당이니까요. 다만 그 이득은 vLLM을 쓰는 순간 이미 받은 것이고, 손잡이를 돌려서 더 받을 몫은 없다는 이야기입니다. 목록들은 이 항목을 몇 배씩 돌려주는 항목들 옆에 놓습니다. 같은 급이 아닙니다.

12개를 한 표에 못 담는 이유

저도 여기서 함정에 빠질 뻔했습니다. 프리픽스 재사용에서 9.58배를 재고 나면, 같은 리그에서 나온 용량 배수들 옆에 그 값을 놓고 싶어집니다. fp8 KV의 용량 2.0배, TurboQuant 3비트의 3.5배, 하이브리드 Mamba의 동시 처리 3.6배 말입니다. 그 표에 올리면 프리픽스 재사용이 1위가 됩니다.

그 표에 올라갈 값이 아닙니다. 9.58배는 프리필이 많은 워크로드에서 재는 벽시계 시간의 단축입니다. 3.5배는 용량 배수, 즉 토큰이 몇 개 들어가느냐입니다. 단위가 다른 두 양이고, 한쪽에서 이기는 기법이 다른 쪽에서는 아무 도움이 되지 않을 수 있습니다. 용량 3.5배를 주는 양자화는 이 카드에서 배치 처리량을 절반 가까이 가져갔습니다. 프리픽스 재사용이 용량에 미친 영향은 0입니다.

그러니 12개는 순위표로 정렬되지 않습니다. 무엇으로 값을 치르는지에 따라 갈립니다.

지불 단위기법이 리그에서의 값
들어가는 토큰 수GQA/MQA, MLA, 레이어 간 공유, 하이브리드, 양자화, 축출, 슬라이딩 윈도2.0~3.5배(양자화, 인용), 3.6배(하이브리드, 인용), 4배(GQA, 계산)
공유 프리픽스 작업의 시간프리픽스 재사용1.00~9.58배(오늘 측정)
디코드 한 스텝의 메모리 트래픽희소 읽기재지 않음
이미 갖고 있으면 아무것도paged 블록 크기1% 미만(오늘 측정)

GQA 행은 측정이 아니라 계산이며, 그 점을 분명히 적어 둡니다. Qwen3-8B은 쿼리 헤드 32개를 KV 헤드 8개 위에서 돌립니다. 이것을 KV 헤드 32개로 되돌리면 토큰당 589,824바이트가 되고, 같은 54.94 GiB가 400,032토큰 대신 100,008토큰을 담습니다. 40K 요청 동시 처리는 9.77개에서 2.44개로 내려갑니다. 4대1 그룹 비율이 정확히 4배를 만들어 주는 셈이고, 이 시리즈에서 산수만으로 이야기가 끝나 측정이 필요 없는 자리는 여기 하나입니다.

3편에서 다룰 절벽

결과를 하나 더 붙입니다. 목록에 더해지는 종류가 아니라 조언을 바꾸는 종류라서입니다.

서로 다른 8,192토큰 문서 64개로 작업 집합을 만들었습니다. 400,032토큰 캐시에 540,672토큰을 넣은 것이고, 전체를 두 바퀴 돌렸습니다. 이 작업 집합의 4분의 3은 캐시에 들어갑니다. 재사용 이득이 초과분에 비례해서 줄어드는 것이라면 2회차의 상당 부분은 캐시에서 나와야 합니다.

1회차 49.458초, 2회차 49.652초. 배수로 0.996배이고, 다시 말해 아무 이득도 없었습니다.

용량을 35% 넘겼는데 이득의 35%를 잃은 것이 아닙니다. 전부 잃었습니다. 모든 문서가 다시 차례가 오기 전에 축출되어서, 2회차 전체를 처음부터 다시 계산했습니다. 그러니 실제로 쓸 수 있는 조언은 "프리픽스 캐싱을 켜라"가 아니라 "작업 집합을 캐시 안에 두라"입니다. 그리고 이 두 문장은 제가 방금 넘은 경계에서 서로 다른 말이 됩니다.

재지 못한 것

오프로딩은 12번째 항목인데, 이 항목에는 제가 낼 숫자가 없습니다. vLLM 0.28.0의 SimpleCPUOffloadConnector가 제가 시도한 설정 열한 가지에서 모두 깨졌습니다. eager와 lazy 모드, 호스트 용량 8GB부터 64GB까지, 작업 집합은 GPU 캐시의 16%부터 135%까지 바꿔 봤습니다. 커넥터는 매번 정상적으로 초기화되어 블록 할당 로그까지 남긴 다음, 생성 도중에 세그폴트로 죽었습니다. 생성을 끝까지 마친 한 번은 종료 시점에 죽었습니다.

원인은 특정하지 못했습니다. vLLM 이슈 #56396이 보고한 블록 크기 불일치는 아닙니다. Qwen3-8B의 full attention 레이어 36개는 KV 캐시 그룹 하나를 이루고, 그러면 스케줄러 블록 크기와 해시 블록 크기가 둘 다 16으로 잡혀 나눗셈이 맞습니다. kv_role도 커넥터 소스가 명시한 값을 썼습니다. 스택 트레이스는 얻지 못했습니다. vLLM이 자체 SIGSEGV 핸들러를 설치하는데, 크래시는 별도 엔진 프로세스에서 나기 때문입니다.

제가 말할 수 있는 것은 여기까지입니다. 그 이상으로 넘겨 말하지 않겠습니다. 이 리그에서 이 버전으로는 CPU KV 오프로딩을 돌리지 못했습니다. 이 커넥터를 건드리는 PR과 이슈가 2026년 9월 1일부터 11일 사이에 업스트림에 12건 올라왔고, 그중 하나는 원인이 다른 크래시 보고입니다. 아이디어에 대한 판정이 아니라 아직 공사 중인 기능이라는 뜻입니다.

다음 편

2편은 엔진 쪽 축들을 한 표에 올립니다. 워크로드 모양을 바꿔 가며 재는 프리픽스 재사용, 양자화 프리셋, 블록 크기, 그리고 오프로딩이 돌아가게 되면 그것이 실제로 하는 일입니다. 3편은 조합입니다. 축출과 프리픽스 재사용이 부딪히는 지점, 위에서 본 용량 절벽, 그리고 네 가지 워크로드 모양에서 무엇을 켜야 하는지를 다룹니다.

다음 편을 받고 싶으시면 아래 구독 폼을 쓰시면 됩니다. 구독할 이유는 이 연재 방식 자체에 있습니다. 어떤 측정이 이번 편에 쓴 내용을 반박하면, 그 이야기를 다음 편에서 읽게 되실 겁니다.

리그: A100 80GB PCIe × 1, 드라이버 535.288.01 / CUDA 12.2. 소프트웨어: vLLM 0.28.0(+cu129), transformers 5.16.1, Qwen3-8B bf16, max_model_len 40,960, gpu_memory_utilization 0.90. 프리픽스 워크로드: 요청 32개, 고유 접미사 256토큰, 출력 64토큰, greedy. 측정 스크립트와 원시 JSON은 drafts/에 있습니다.

더 많은 콘텐츠를 받아보세요

SNS에서 새로운 글과 튜토리얼 소식을 가장 먼저 받아보세요

이메일로 받아보기

관련 포스트