Local LLM VRAM 모델 스왑 방식
질문: llama.cpp/ollama로 LLM을 돌릴 때, 모델 전환 시 VRAM에 올린 채 swap하는가, 내리고 다시 올리는가? (API 호출 자체는 논외 — 인프라 레벨 질문)
결론부터
둘 다 "내리고 다시 올리는" 방식이야. VRAM 안에서 in-place swap 없음.
모델 A 실행 중 (VRAM 점유)
→ 모델 B 요청
→ 모델 A unload (VRAM 해제)
→ 모델 B load (수 초~수십 초 소요)
→ 모델 B 실행
llama.cpp
기본 동작
- 서버 시작 시 지정된 모델 1개를 VRAM에 로드
- 해당 모델로 요청 처리
- 다른 모델로 전환하려면 서버 재시작 필요 (기존 방식)
router 모드 (2025년 말 추가)
llama-server --models-dir ./models -c 8192 -ngl 99
- 여러 모델을 등록해두고 요청 시 자동 load/unload
- API:
POST /models/load {"model": "qwen3-7b.gguf"} POST /models/unload {"model": "qwen3-7b.gguf"} GET /models → loaded/loading/unloaded 상태 --max-models N: 동시에 VRAM에 올려둘 모델 수 (기본 1)- 첫 요청 시 자동 load, 동일 모델 재요청은 즉시 처리
주의
--max-models로 2개 이상 동시 로드 가능하나 VRAM 여유 필요- RTX 5080 16GB에서 7B + VLM 동시 → 거의 불가능
- 모델 교체 시 unload 후 load = VRAM 재할당
- VRAM 부족 시 자동 eviction 미지원 (수동 unload 필요)
- 자동 VRAM 기반 eviction은 커뮤니티 feature request로 논의 중
Ollama
기본 동작 (LRU 자동 eviction)
OLLAMA_KEEP_ALIVE=5m # 기본값: idle 모델을 5분간 VRAM 유지
OLLAMA_KEEP_ALIVE=0 # 요청 후 즉시 unload
OLLAMA_KEEP_ALIVE=30s # 30초 유지
- 새 모델 요청 시 VRAM 부족하면 LRU(가장 오래 사용 안 된) 모델 먼저 unload
- 사람이 직접 관리 안 해도 됨 (llama.cpp router보다 편함)
- 내부는 llama.cpp wrapper
swap 시 지연
- 2~10초 (모델 크기에 따라 다름)
- 자주 전환하면 성능 저하
전략 선택 기준
| 상황 | 전략 | |------|------| | 모델 항상 1개만 사용 | 단일 llama-server, unload 없음 | | 가벼운 모델 상시 + 가끔 무거운 모델 | Ollama keep-alive 조정 | | 명시적 제어 필요 | llama.cpp router 모드 + /models/load | | 동시 여러 모델 유지 | VRAM이 허용할 때만 --max-models |
내 일정 프로그램에 적용하면
RTX 5080 16GB 기준:
상시: Qwen 7B (경량 작업, 과제 파싱 등) ~8GB
→ keep-alive 길게 설정 (항상 VRAM 상주)
필요 시: VLM (묵연 PDF 변환) ~12GB 이상
→ Qwen 먼저 unload → VLM load (20~60초)
→ VLM 작업 완료 → Qwen 다시 load
판단 필요: Claude API
→ VRAM 무관, 항상 사용 가능
핵심 설계 결정: LLM 라우터가 VRAM 상태를 체크하고 작업 큐를 직렬화해야 함. 동시 LLM 요청은 불가.
작업 큐 직렬화 패턴 (Python pseudo)
class VRAMRouter:
def __init__(self):
self.current_model = "qwen-7b" # 상시 상주
self.queue = asyncio.Queue()
async def route(self, task):
if task.requires_vlm:
# 1. 현재 모델 unload
await llama_server.unload(self.current_model)
# 2. VLM load (20~60초)
await llama_server.load("vlm-model")
# 3. 작업 실행
result = await run_vlm(task)
# 4. 다시 경량 모델로 복귀
await llama_server.unload("vlm-model")
await llama_server.load("qwen-7b")
return result
elif task.requires_judgment:
# VRAM 무관
return await claude_api.complete(task)
else:
# 상시 상주 Qwen 직접 사용
return await llama_server.complete(task)
pi-ai와의 관계
pi-ai의 getModel(), complete(), stream()은 어느 모델에 요청을 보낼지 결정하는 라우팅 레이어야.
실제 VRAM 관리는 pi-ai 범위 밖 — llama.cpp/ollama 서버가 담당.
pi-ai (라우팅 결정):
작업 유형 → 모델 선택 → API 호출
llama-server / ollama (VRAM 관리):
요청 받으면 필요한 모델 load/unload 처리
두 레이어가 분리되어 있음. pi SDK를 쓰든 직접 구현하든 VRAM 관리 로직은 별도로 작성해야 한다.
관련
- research/pi-sdk/overview — pi SDK 전체 분석
- VLM OOM 복구: subprocess ADR-001 (mugyeon decisions.md 참고)