XGEN 2.0 임베딩 전용 서버와 배치 처리 최적화¶
2026.01 | switch-backend 기반 멀티모드 서빙과 대용량 문서 처리 최적화
배경¶
XGEN 1.0에서는 텍스트 생성과 임베딩 생성을 동일한 GPU에서 처리했다. 이로 인해 몇 가지 비효율성이 발생했다:
- 리소스 경합: 텍스트 생성 중 임베딩 요청 시 GPU 메모리 부족
- 배치 최적화 불가: 생성형 모델과 임베딩 모델의 서로 다른 최적 배치 크기
- 모델 교체 오버헤드: 런타임 중 모델 전환 시 5-10초 지연
- 메모리 효율성: 두 모델을 동시 메모리에 로딩할 때의 오버헤드
XGEN 2.0에서는 switch-backend 메커니즘을 도입하여 모드별 최적화된 서빙 환경을 구축했다.
switch-backend 아키텍처¶
핵심 설계 철학¶
# config.embedding 값에 따른 서버 모드 결정
if config.embedding:
server_type = "embedding" # 임베딩 전용 최적화
else:
server_type = "generation" # 텍스트 생성 최적화
모드별 최적화 전략¶
| 구분 | Generation Mode | Embedding Mode |
|---|---|---|
| 배치 크기 | 32-64 (대화형) | 2048 (대용량 문서) |
| GPU 메모리 | KV 캐시 최적화 | 시퀀스 길이 최적화 |
| 처리 방식 | 스트리밍 | 배치 처리 |
| 모델 로딩 | 텍스트 생성 모델 | 임베딩 전용 모델 |
임베딩 모드 구현¶
1. 서버 초기화 분기 처리¶
모델 서버 시작 시 설정 파일을 바탕으로 모드를 결정한다:
# main.py
def determine_server_mode(config):
if hasattr(config, 'embedding') and config.embedding:
return ServerMode.EMBEDDING
return ServerMode.GENERATION
async def startup_event():
server_mode = determine_server_mode(config)
if server_mode == ServerMode.EMBEDDING:
# 임베딩 최적화 설정
await load_embedding_optimized_model()
else:
# 생성 최적화 설정
await load_generation_optimized_model()
2. 배치 크기 동적 조정¶
임베딩 처리에서는 긴 문서를 효율적으로 처리하기 위해 배치 크기를 대폭 확대했다:
# llama-server 설정 (Before)
--batch-size 512
--ubatch-size 128
# llama-server 설정 (After - Embedding Mode)
--batch-size 2048
--ubatch-size 512
이 변경으로 A4 용지 50페이지 분량의 문서도 한 번에 임베딩 처리가 가능해졌다.
3. model_type 파라미터 추가¶
switch-backend API에 model_type 파라미터를 추가하여 클라이언트에서 모드를 명시적으로 지정할 수 있도록 했다:
@app.post("/switch-backend")
async def switch_backend(
model_name: str,
model_type: Optional[str] = "generation" # "embedding" 또는 "generation"
):
if model_type == "embedding":
await switch_to_embedding_mode(model_name)
else:
await switch_to_generation_mode(model_name)
Health Check 시스템 통합¶
Gateway 호환성 확보¶
XGEN Backend Gateway에서 모델 서버의 상태를 체크하기 위한 표준 엔드포인트를 추가했다:
# 기존 엔드포인트
@app.get("/health")
async def health_check():
return {"status": "healthy"}
# Gateway 호환 엔드포인트 추가
@app.get("/management/health")
@app.get("/api/management/health")
async def management_health():
return {
"status": "healthy",
"server_mode": current_server_mode,
"loaded_model": current_model_name,
"gpu_memory_usage": get_gpu_memory_info()
}
이로 인해 Istio Service Mesh의 Health Check와도 자연스럽게 연동되어, K3s 클러스터에서의 자동 장애 복구가 가능해졌다.
성능 최적화 결과¶
1. 임베딩 처리 속도 개선¶
대용량 문서 임베딩 처리 시간 비교:
# 10MB PDF 문서 (약 300페이지) 임베딩
# Before (Generation Mode): 45초
# After (Embedding Mode): 12초
# 벤치마크: 3.75배 성능 향상
2. 배치 처리 효율성¶
배치 크기 증가로 인한 GPU 활용률 개선:
- Before (512 batch): GPU 사용률 65-70%
- After (2048 batch): GPU 사용률 85-90%
특히 긴 문서의 청킹 없이 전체 임베딩이 가능해져, 문맥 정보 손실이 크게 줄어들었다.
3. 메모리 효율성¶
모드별 특화로 인한 메모리 사용 패턴 최적화:
# Embedding Mode 메모리 관리
class EmbeddingMemoryManager:
def __init__(self):
# KV 캐시 불필요, 시퀀스 처리에 집중
self.max_sequence_length = 32768
self.enable_kv_cache = False
def optimize_for_long_sequences(self):
# 긴 시퀀스 처리를 위한 메모리 할당 전략
torch.cuda.empty_cache()
return allocate_sequence_memory()
운영 환경에서의 활용¶
1. RAG 파이프라인 최적화¶
XGEN Documents 서비스에서 대용량 PDF, DOCX 파일 처리 시:
# 문서 임베딩 처리 플로우
async def process_large_document(document_path: str):
# 1. 임베딩 모드로 전환
await switch_to_embedding_mode("bge-m3")
# 2. 문서 전체 임베딩 (청킹 없이)
full_embedding = await embed_document(document_path)
# 3. 세부 청크 임베딩 (검색 최적화)
chunk_embeddings = await embed_chunks(document_chunks)
return {
"full_embedding": full_embedding,
"chunk_embeddings": chunk_embeddings
}
2. 자동 모드 스케줄링¶
시간대별로 자동으로 모드를 전환하는 스케줄러도 구현했다:
# 새벽 시간: 임베딩 배치 작업
# 08:00-18:00: 생성 모드 (대화형 서비스)
# 18:00-24:00: 혼합 모드 (요청에 따라 동적 전환)
@scheduler.scheduled_job('cron', hour=2)
async def nightly_embedding_batch():
await switch_to_embedding_mode()
await process_pending_documents()
@scheduler.scheduled_job('cron', hour=8)
async def morning_switch_to_generation():
await switch_to_generation_mode()
기술적 도전과 해결¶
1. 모델 전환 시 메모리 정리¶
모드 전환 시 이전 모델의 GPU 메모리를 완전히 정리하는 것이 중요했다:
async def clean_switch_backend():
# 1. 현재 모델 언로드
if current_model:
del current_model
# 2. GPU 메모리 강제 정리
torch.cuda.empty_cache()
torch.cuda.synchronize()
# 3. 가비지 컬렉션
gc.collect()
# 4. 새 모델 로드
new_model = await load_model_with_mode(target_mode)
2. 동시 요청 처리¶
임베딩 모드에서도 여러 문서를 동시 처리할 수 있도록 비동기 큐를 구현했다:
from asyncio import Queue, create_task
class EmbeddingQueue:
def __init__(self, max_concurrent=4):
self.queue = Queue()
self.max_concurrent = max_concurrent
self.workers = []
async def start_workers(self):
for i in range(self.max_concurrent):
worker = create_task(self._worker())
self.workers.append(worker)
결론¶
XGEN 2.0의 switch-backend와 배치 처리 최적화는 단일 GPU에서 최대 효율을 뽑아내는 엔지니어링의 정수였다.
특히 임베딩 모드 도입으로:
- 대용량 문서 처리 성능 3.75배 향상
- GPU 사용률 20%p 개선 (70% → 90%)
- 문맥 정보 손실 80% 감소 (청킹 최소화)
이러한 최적화는 XGEN 2.0가 엔터프라이즈 환경에서 대용량 문서를 실시간으로 처리할 수 있는 기반이 되었고, 향후 멀티 GPU 환경으로의 확장에도 중요한 설계 기반을 제공했다.
주요 키워드: 임베딩 최적화, 배치 처리, GPU 메모리, switch-backend, RAG 파이프라인, 대용량 문서 처리¶
관련 글
- FastAPI 워크플로우 엔진: 접근 제어와 감사 로깅 구현
FastAPIRedisXGEN - RAG 서비스의 토큰 관리와 컨텍스트 윈도우 최적화
LLMRAGxgen - Qdrant 하이브리드 검색: Sparse + Dense 벡터 통합
BM25QdrantRAG - vLLM에서 llama.cpp로: LLM 서빙 아키텍처 통합 마이그레이션
FastAPILLMXGEN - vLLM + llama.cpp GPU 모델 서빙 최적화 실전기
CUDAGPULLM