RDNA3용 llama.cpp Flash Attention: Vulkan f16 K 대비 KV VRAM 47% 절감, F16 K/q4_0 V에서 거의 손실 없는 KLD. 1부.
Flash Attention for llama.cpp on RDNA3: 47% less KV VRAM than Vulkan f16 K, KLD almost losselss on F16 K / q4_0 V. Part 1.
핵심 요약
RDNA3 GPU에서 K 캐시를 8비트로 재패킹하여 VRAM 사용량을 획기적으로 줄이고 품질 손실을 최소화한 새로운 최적화 기법입니다.
- VRAM 효율성 — K 캐시를 8비트로 재패킹하여 128k 컨텍스트에서 VRAM 사용량을 크게 절감함
- 품질 유지 — fp16 스케일링을 유지하여 KLD 수치가 fp16 대비 거의 차이 없는 수준을 보임
- 기술적 구현 — GPU의 네이티브 sudot4 명령어를 활용해 실시간으로 int8 재패킹 및 연산 수행
- 향후 과제 — RDNA4 지원을 위한 포팅 및 MTP 모드에서의 성능 최적화 필요
llama.cpp 어텐션에서 원래 하던 고민이 뭐냐면, KV 캐시를 양자화해서 품질을 떨굴지, 아니면 fp16으로 유지하면서 VRAM을 다 태워 먹을지 둘 중 하나였거든. 근데 이제 RDNA3에서는 세 번째 선택지가 생겼다! 8비트 K 값 4개를 32비트 하나에 때려 박아서 GPU의 네이티브 sudot4 내적 명령어에 바로 꽂아버리는 방식이야. K에 대한 손실 있는 양자화도 없고, 메모리에 fp16 K 버퍼를 띄워놓을 필요도 없어. 커널이 딱 필요한 데이터 레이아웃으로 가져가니까, 전체 fp16 K 텐서 대신 8비트 K 페이로드랑 fp16 스케일만 저장하면 돼서 VRAM 사용량이 확 줄어든다.
근데 진짜 차이는 MTP 드래프트 모델을 돌리면서 128k 컨텍스트를 쓸 때 확 드러나. 이제 메인 모델이랑 드래프트 모델, 이렇게 두 개의 풀 컨텍스트에 대한 K랑 V를 다 저장해야 하거든. rocm-smi로 측정한 총 VRAM 사용량은 이래:
128k active MTP, q4_0 V both sides |
| Vulkan f16 K | 23.18 GiB | 22.50 GiB |
| ROCm packed16 K** | 21.76 GiB |
저 1.42 GiB 차이가 128k MTP 세션을 돌릴 수 있냐 없냐를 가르는 결정적인 차이야. 다른 VRAM 압박 상황에 따라 다르겠지만 말이지. 모델 웨이트를 줄인 게 아니라서 모델 성능은 똑같아. 순수하게 두 컨텍스트 전체에서 K-캐시 메모리 점유율을 깎아낸 결과지.
이제 품질 쪽을 보자. packed16 K 방식은 디퀀타이즈(dequant) 후에 fp16 범위의 K 값을 그대로 뽑아내. 8비트 패킹은 손실 있는 양자화가 아니라 그냥 저장 레이아웃을 바꾼 거거든. 압축 손실은 V 쪽에서만 발생해. 27B 모델, ctx=512, chunks=4 조건에서 WikiText-2로 측정했고, V=q4_0이랑 V=q8_0을 V=fp16 베이스라인이랑 비교했어. K는 모든 후보에서 packed16 I32를 썼고:
| Metric | Value |
| Mean PPL ratio | 1.0020 ± 0.0042 |
| Mean KLD | 0.00455 ± 0.00034 |
| Median KLD | 0.00182 |
| 99th percentile KLD | 0.0500 |
| Same top token | 97.06% |
| RMS Δp | 1.98% |
q8_0 V vs fp16 V:
| Metric | Value |
| Mean PPL ratio | 1.0010 ± 0.0034 |
| Mean KLD | 0.00283 ± 0.00033 |
| Median KLD | 0.00086 |
| 99th percentile KLD | 0.0313 |
| Same top token | 97.94% |
| RMS Δp | 1.68% |
이 KLD 수치가 뭔 소린지 설명하자면, Kullback-Leibler divergence는 두 확률 분포가 얼마나 다른지 재는 지표야. 보통 0.01 이하면 토큰 단위 분포에서는 사실상 구분이 안 된다고 봐. 둘 다 그 범위 안으로 아주 쾌적하게 들어오고, q8_0이 q4_0보다 발산 수치가 절반 정도 낮아(평균 0.0028 vs 0.0046, 중앙값 0.0009 vs 0.0018). VRAM 아끼려고 q4_0 V를 쓰면 fp16 K+V보다 VRAM은 적게 먹으면서 KLD는 0.0045 정도 손해 보는 셈이지. 더 높은 품질을 원하면 q8_0 V를 써서 KLD 0.0028 정도로 타협할 수 있고(K 절약분은 똑같으니까 V 포맷만 바뀐다고 보면 됨).


