왜 VRAM이 병목이 되는가
LLM 추론에서 VRAM은 가중치, KV 캐시, 활성값(activation) 세 가지에 소비된다. 예를 들어 13B 모델을 FP16으로 올리면 가중치만 약 26GB이고, 여기에 시퀀스 길이와 배치에 비례하는 KV 캐시가 더해진다. 24GB급 소비자 GPU에서는 가중치조차 다 올라가지 않아 OOM이 난다. 양자화(INT8/INT4)로 줄이는 방법이 우선이지만, 정밀도 손실이나 지원 커널 부재로 한계가 있을 때 남은 카드가 CPU 오프로딩이다.
CPU 오프로딩의 기본 원리
핵심은 "모든 텐서를 GPU에 둘 필요는 없다"는 관찰이다. 자주 접근하지 않거나, 연산량 대비 데이터가 큰 일부 레이어·텐서를 시스템 RAM에 두고, 필요할 때만 PCIe를 통해 GPU로 옮긴다. 트레이드오프는 명확하다. VRAM은 아끼지만 PCIe 전송이 지연으로 추가된다. 따라서 오프로딩 대상은 연산 밀도가 낮고 전송 빈도가 낮은 텐서부터 고르는 것이 원칙이다.
llama.cpp: 레이어 단위 오프로딩
GGUF 기반 llama.cpp는 -ngl(n-gpu-layers)로 GPU에 올릴 트랜스포머 레이어 수를 직접 지정한다. 나머지는 CPU가 처리한다. 전체 레이어가 40개라면 아래처럼 30개만 GPU에 올려 VRAM을 조절한다.
./llama-server \
--model qwen2.5-14b-instruct-q4_k_m.gguf \
--n-gpu-layers 30 \
--ctx-size 8192 \
--flash-attn \
--port 8080
# VRAM이 부족하면 --n-gpu-layers를 줄이고,
# 여유가 있으면 늘려 처리량을 회복한다 (999 = 전부 GPU)
KV 캐시까지 CPU로 미루고 싶지 않다면 --no-kv-offload 없이 두고, 컨텍스트를 줄이는 편이 지연에 유리하다. KV 캐시는 매 토큰 접근되므로 오프로딩 시 비용이 크다.
transformers/accelerate: device_map
Hugging Face 스택에서는 accelerate의 device_map으로 자동 배치한다. "auto"는 GPU를 먼저 채우고 넘치는 부분을 CPU, 그다음 디스크로 내린다.
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B-Instruct",
dtype="float16",
device_map="auto",
max_memory={0: "20GiB", "cpu": "48GiB"}, # GPU 상한을 명시
offload_folder="./offload", # 넘치면 디스크로
)
print(model.hf_device_map) # 레이어별 배치 확인
max_memory로 GPU 상한을 실제 가용량보다 약간 낮게(활성값 여유분) 잡는 것이 OOM 회피의 요령이다.
무엇을 먼저 오프로딩할 것인가
텐서마다 오프로딩 적합도가 다르다. 실무 판단 기준을 정리하면 다음과 같다.
| 대상 | 접근 빈도 | 오프로딩 적합도 |
|---|---|---|
| 후반부 트랜스포머 레이어 | 토큰당 1회 | 보통 (레이어 순차 실행이라 감내 가능) |
| 임베딩/LM head | 시작·끝 1회 | 높음 |
| KV 캐시 | 매 토큰 전 레이어 | 낮음 (지연 급증) |
| MoE 비활성 전문가 | 드묾 | 높음 |
특히 MoE 모델은 토큰마다 일부 전문가만 활성화되므로, 비활성 전문가를 CPU에 두는 전략이 VRAM 대비 지연 손해가 가장 적다.
실무 주의점
첫째, PCIe 대역폭이 곧 성능이다. Gen3 x4 같은 좁은 링크에서는 오프로딩 지연이 크게 늘어나므로 대역폭을 먼저 확인한다. 둘째, CPU가 실제로 연산을 맡으므로 코어 수와 메모리 대역폭이 처리량에 직접 영향을 준다. 셋째, 디스크 오프로딩(offload_folder)은 최후의 수단이다. NVMe라도 RAM 대비 수십 배 느려 대화형 서비스에는 부적합하다. 넷째, 오프로딩은 배치 처리량을 크게 떨어뜨리므로, 고TPS가 필요한 서비스라면 더 작은 양자화 모델이나 GPU 추가가 총비용 면에서 나을 수 있다. 오프로딩은 "가진 하드웨어로 더 큰 모델을 돌리기 위한 타협"이라는 성격을 명확히 이해하고 적용해야 한다.