/AI & 자동화/vLLM vs TGI 심층 비교: 배치 사이즈와 동시 요청에 따른 LLM 추론 최적화 완벽 가이드
AI & 자동화vLLMTGI

vLLM vs TGI 심층 비교: 배치 사이즈와 동시 요청에 따른 LLM 추론 최적화 완벽 가이드

LLM 서비스의 성능 병목 지점을 이해하고, vLLM과 TGI를 활용하여 배치 사이즈와 동시 요청 변화에 따른 GPU 메모리 사용량 및 처리량을 측정하는 실전 벤치마크 방법을 안내합니다.

vLLM vs TGI 심층 비교: 배치 사이즈와 동시 요청에 따른 LLM 추론 최적화 완벽 가이드

vLLM vs TGI 심층 비교: 배치 사이즈와 동시 요청에 따른 LLM 추론 최적화 완벽 가이드

LLM을 기반으로 서비스를 구축하는 과정에서 가장 까다롭고, 동시에 가장 비용을 많이 차지하는 부분이 바로 '추론(Inference)' 단계입니다. 모델을 학습시키는 것과 실제로 사용자 요청에 응답하는 추론 과정은 요구되는 최적화 포인트가 완전히 다릅니다. 단순히 모델을 로드하는 것만으로는 충분하지 않습니다. 우리는 '최대 동시 사용자 수(Concurrency)'와 '최적의 배치 크기(Batch Size)'라는 두 가지 변수를 통제하며, GPU 자원을 100% 활용하는 아키텍처를 설계해야 합니다.

이 가이드는 현업 ML 엔지니어와 MLOps 개발자 분들을 위해, 업계 표준으로 자리 잡은 vLLM과 TGI(Text Generation Inference)를 활용하여 추론 성능을 체계적으로 벤치마크하고, 메모리 병목 지점을 찾아내는 실질적인 방법을 제시합니다.

LLM 추론 성능 최적화, 왜 가장 어려운가?

LLM 추론의 성능 저하 원인을 이해하는 것이 최적화의 첫걸음입니다. 일반적인 컴퓨팅 작업은 입력 데이터 크기(Batch Size)를 늘리면 처리량이 선형적으로 증가하는 경향이 있습니다. 하지만 LLM은 '토큰 생성'이라는 특성 때문에 복잡성이 추가됩니다.

💡 핵심 원리: KV Cache와 PagedAttention의 마법

LLM이 텍스트를 생성하는 과정은 '토큰을 하나씩 예측'하는 과정의 반복입니다. 이 과정에서 이전에 생성된 모든 토큰의 키(Key)와 값(Value) 벡터를 저장해두어야 하는데, 이것이 바로 KV Cache입니다.

만약 KV Cache를 비효율적으로 관리한다면, GPU 메모리는 순식간에 고갈됩니다. 여기서 vLLM과 TGI가 혁신을 가져온 부분이 바로 PagedAttentionContinuous Batching입니다.

  1. PagedAttention (메모리 관리 혁신): 기존의 메모리 할당 방식은 고정된 블록 단위로 메모리를 예약했습니다. 이는 실제 사용량보다 훨씬 많은 메모리를 낭비하게 만들었습니다. PagedAttention은 운영체제의 가상 메모리 개념을 차용하여, KV Cache를 마치 페이지 단위로 관리합니다. 필요한 만큼만, 정확하게 할당하고 해제하기 때문에 메모리 단편화(Fragmentation) 문제를 획기적으로 줄이고 메모리 효율성을 극대화합니다.
  2. Continuous Batching (처리량 극대화): 전통적인 배치 처리는 '가장 느린 요청'이 끝날 때까지 전체 배치를 대기시키는 방식이었습니다. 만약 10개의 요청 중 1개가 매우 느리게 응답한다면, 나머지 9개 요청은 그 속도에 맞춰 대기해야 합니다. Continuous Batching은 이 개념을 깨고, 요청이 완료되는 즉시 해당 슬롯을 비우고 새로운 요청을 투입합니다. 이는 GPU 자원을 낭비하는 시간을 최소화하여 처리량(Throughput)을 극대화합니다.

이 두 가지 기술 덕분에, 우리는 단순히 '배치 사이즈를 키우는 것' 이상의 차원, 즉 '자원 할당의 효율성'과 '유휴 시간 제로화'를 목표로 삼을 수 있게 된 것입니다.

실전 벤치마크 환경 구축: vLLM vs TGI 활용

성능을 비교하려면, 단순히 API를 호출하는 것을 넘어 변수를 통제하며 반복 측정하는 시스템이 필요합니다. 우리는 다음 두 가지 핵심 변수를 조작해야 합니다.

  1. Batch Size (BS): 한 번에 처리할 요청의 개수 (동일한 시점에 처리되는 요청의 크기).
  2. Concurrency (C): 시스템이 동시에 유지하고 처리할 수 있는 최대 요청의 개수 (시스템의 동시 접속 처리 능력).

🛠️ 실행 가능한 벤치마크 로직 (Python Snippet)

아래 코드는 vLLM을 예시로 들었으며, 핵심 로직은 TGI에서도 유사하게 적용 가능합니다. 이 코드는 반복문(for loop)을 통해 다양한 조합을 테스트하고, 시스템 리소스를 측정하는 프레임워크를 제공합니다.

Python
import time
import psutil
import os
from vllm import LLM, SamplingParams
# from nvidia.system import nvidia_smi # 실제 환경에 맞게 사용

# --- 설정 변수 ---
MODEL_NAME = "meta-llama/Llama-2-7b-hf" # 테스트할 모델 경로
MAX_BATCH_SIZE = 16
MAX_CONCURRENCY = 32
START_TOKEN_COUNT = 100 # 각 요청이 생성할 평균 토큰 수

# vLLM 모델 로드 (GPU 메모리 점유 확인 필요)
print("모델 로딩 중... (이 과정에서 GPU 메모리 할당이 발생합니다)")
llm = LLM(model=MODEL_NAME, dtype="half", trust_remote_code=True)
sampling_params = SamplingParams(temperature=0.7, top_p=0.95, max_tokens=START_TOKEN_COUNT)

# 벤치마크 결과 저장용 리스트
results = []

print("\n=====================================================")
print("🚀 벤치마크 시작: BS와 Concurrency 변화에 따른 측정")
print("=====================================================")

# 1. 배치 사이즈(BS)와 동시 요청 수(C)를 조합하여 반복 테스트
for bs in range(1, MAX_BATCH_SIZE + 1, 4): # BS는 4 단위로 증가시켜 테스트
    for conc in range(1, MAX_CONCURRENCY + 1, 8): # Concurrency는 8 단위로 증가시켜 테스트
        
        print(f"\n[테스트 조합] Batch Size: {bs}, Concurrency: {conc}")
        
        # --- 벤치마크 실행 로직 ---
        start_time = time.time()
        
        # 시뮬레이션: 'conc'개의 요청을 'bs' 크기의 배치로 처리하는 과정을 시뮬레이션
        # 실제로는 비동기(asyncio)를 사용하여 'conc'개의 독립적인 요청을 유지하며 부하를 줍니다.
        dummy_inputs = ["A simple prompt for testing performance."] * conc
        
        # vLLM의 generate_batch를 사용하여 부하를 줍니다.
        outputs = llm.generate_batch(dummy_inputs, sampling_params)
        
        end_time = time.time()
        
        # --- 성능 측정 및 기록 ---
        elapsed_time = end_time - start_time
        total_tokens_generated = len(outputs) * START_TOKEN_COUNT
        
        # 처리량 (Throughput): 토큰/초
        throughput = total_tokens_generated / elapsed_time
        
        # 메모리 사용량 측정 (실제로는 주기적으로 psutil.Process().memory_info()를 체크해야 함)
        # 여기서는 단순화하여, 메모리 사용량은 'Concurrency'와 'Model Size'에 비례한다고 가정합니다.
        estimated_memory_usage = (conc * 0.005) + (bs * 0.001) # 가상 계산
        
        results.append({
            "BS": bs, 
            "Concurrency": conc, 
            "Throughput (Tokens/sec)": throughput, 
            "Memory Usage (GB)": estimated_memory_usage
        })

print("\n=====================================================")
print("✅ 벤치마크 완료. 결과 분석 단계로 이동합니다.")

📊 성능 비교 테이블: 최적 조합 찾기

위의 반복 테스트를 통해 얻은 데이터를 구조화하면 다음과 같은 성능 비교표를 얻을 수 있습니다. 이 표는 모델, 하드웨어, 그리고 코드 최적화에 따라 값이 크게 달라지므로, 실제 테스트를 통해 값을 채워 넣어야 합니다.

Batch Size (BS)Concurrency (C)Throughput (Tokens/sec)Memory Overhead (GB)최적화 코멘트
81612524적절한 균형점. GPU 활용률 높음.
16811028배치 크기 증가로 인해 메모리 경합 발생.
43210522동시성(Concurrency)을 높여 처리량 유지.
81612524최적 지점 (Sweet Spot)으로 추정됨.

💡 분석 포인트:

  1. Throughput (처리량): 가장 높은 값을 가진 조합이 현재 시스템의 최적 운영 지점(Sweet Spot)일 가능성이 높습니다.
  2. Memory Overhead (메모리): 메모리 사용량이 급격히 증가하는 지점은 GPU 메모리 부족(OOM)의 위험 신호이므로, 이보다 낮은 지점을 선택해야 합니다.

🚀 결론 및 최적화 가이드라인

성능 최적화는 처리량(Throughput)을 최대화하면서 메모리 사용량(Memory Overhead)을 안정적으로 유지하는 지점을 찾는 과정입니다.

  1. 배치 크기(Batch Size) vs. 동시성(Concurrency):

    • Batch Size 증가: 한 번에 처리하는 데이터의 양을 늘려 GPU의 병렬 처리 능력을 최대한 활용합니다. (GPU 활용률 극대화)
    • Concurrency 증가: 여러 개의 독립적인 요청을 동시에 처리하여 시스템의 전체 처리량을 높입니다. (시스템 처리량 극대화)
    • 최적화: 두 값을 적절히 조합하여, GPU가 과부하되지 않으면서도 요청을 지연 없이 처리할 수 있는 지점을 찾아야 합니다.
  2. 프레임워크 활용:

    • vLLM 또는 TGI 사용: 만약 LLM 추론을 수행 중이라면, Hugging Face의 기본 라이브러리 대신 vLLM이나 Text Generation Inference (TGI)와 같은 최적화된 서빙 프레임워크를 사용하는 것이 성능 향상에 가장 큰 영향을 미칩니다. 이들은 PagedAttention과 같은 고급 메모리 관리 기법을 사용합니다.

이 가이드를 바탕으로, 실제 서비스 환경의 트래픽 패턴(동시 접속자 수, 평균 요청 크기)을 시뮬레이션하여 반복적인 테스트를 진행하는 것이 가장 중요합니다.

✦ ✦ ✦
편집 검토 · Editorial Review

이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.

작성 · Content Reviewer·검토 · 사람 편집자·발행 · 2026년 6월 4일

댓글

불러오는 중...