본문 바로가기
C.W.K.
Stream
Lesson 04 of 06 · published

여러 모델, 묶음 추론, 메모리 공유

~14 min · batching, multi-model, throughput

Level 0호기심
0 XP0/51 lessons0/15 achievements
0/100 XP to next level100 XP to go0% complete

닮아 보여도 목적이 다른 두 방식

모델 하나에 요청 하나를 보내는 시연을 넘어서면 운영에서 두 방식이 나타나. 서로 관련돼 보이지만 최적화하는 대상이 다르고, 맞지 않는 방식을 쓰면 자원만 낭비해.

  • 프로세스 하나에 여러 모델 — 한 서비스가 대화용 7B 모델과 작은 임베딩 모델, 이미지 이해용 VLM처럼 서로 다른 모델을 함께 제공해. 프로세스의 공통 비용은 아끼지만 모델끼리 통합 메모리를 놓고 경쟁해.
  • 모델 하나의 묶음 추론 — 같은 모델로 들어오는 여러 프롬프트를 한 묶음으로 처리해. 처리량은 늘지만 요청마다 기다리는 시간도 늘어. 전체 계산 효율과 개별 지연 시간을 맞바꾸는 방식이야.

이 레슨에서는 두 방식의 차이와 각각 언제 맞는지 살펴봐.

여러 모델 — 메모리 장부

프로세스 하나에 여러 모델을 두면 모든 가중치가 통합 메모리에 동시에 머물러. foundations.lesson4에서 모델마다 한 어림셈을 그대로 더하면 돼. 7B Q4 약 5GB와 작은 임베딩 모델 약 1GB, 2B VLM 약 2GB를 함께 두면 상주 메모리가 약 8GB고, 현재 생성 중인 모델의 KV 캐시가 더해져. 모델 하나만 쓸 때보다 장부가 더 중요해. 어느 모델이든 갑자기 온전한 추론 예산을 요구할 수 있거든.

잘 작동하는 방식은 시작할 때 모든 모델을 불러와 계속 상주시킨 뒤 요청 종류에 맞춰 보내는 거야. 요청이 올 때 불러오고 놀 때 내리는 방식은 좋지 않아. 큰 모델을 불러오는 데 드는 몇 초가 개별 추론 시간보다 훨씬 길 수 있어.

묶음 추론 — 처리량이 지연 시간보다 중요할 때

같은 모델에 동시 프롬프트가 많다면 묶음으로 GPU 호출 한 번에 더 많은 토큰을 처리해 계산 자원당 처리량을 크게 높일 수 있어. 대신 각 요청은 첫 토큰을 보기 전에 묶음을 채우는 시간이 끝날 때까지 기다려야 해.

mlx-lm에는 일부 PyTorch 서빙 기술처럼 다듬어진 일급 묶음 생성 기능이 아직 없어. 그래서 현실적인 방식은 FastAPI 같은 서비스층에서 비동기로 요청을 모은 뒤 mlx-lm으로 프롬프트마다 추론하는 거야. 짧은 프롬프트가 동시에 많이 들어오는 작업이라면 MLX 위의 더 높은 수준 서빙 기술을 살펴보거나 mlx-lm의 묶음 API가 성숙할 때까지 기다려.

상황별 선택

  • 모델 하나, 사용자 한 명prod.lesson1의 단순한 FastAPI 서비스면 되고 묶음 처리는 필요 없어.
  • 여러 모델, 프로세스 하나, 낮은 동시성 — 시작할 때 모두 불러와 요청 종류에 따라 보내. 대신 가용성을 위해 통합 메모리를 계속 쓴다는 점을 받아들여야 해.
  • 모델 하나, 매우 높은 동시성 — Ollama나 맞춤 묶음 래퍼 같은 묶음 서빙을 살펴봐. 더 높은 처리량을 얻는 대신 요청별 지연 시간이 늘어.
  • 여러 모델과 높은 동시성 — Mac 한 대의 서빙 규모를 넘어섰다는 신호일 때가 많아. 역할 하나씩 맡긴 여러 Mac이 더 경제적인지 따져 봐.

Code

한 FastAPI 프로세스의 여러 모델·python
# Sketch of a multi-model service (extends prod.lesson1's pattern).
from contextlib import asynccontextmanager
from fastapi import FastAPI
from pydantic import BaseModel
from mlx_lm import load, generate

models: dict = {}

@asynccontextmanager
async def lifespan(app: FastAPI):
    print("Loading models...")
    models["chat"], models["chat_tok"] = load("mlx-community/Mistral-7B-Instruct-v0.3-4bit")
    models["small"], models["small_tok"] = load("mlx-community/Llama-3.2-1B-Instruct-4bit")
    # Warm up each
    for name in ["chat", "small"]:
        _ = generate(models[name], models[f"{name}_tok"], prompt="warmup", max_tokens=1)
    print("All models loaded and warm.")
    yield
    models.clear()

app = FastAPI(lifespan=lifespan)

class Req(BaseModel):
    model: str        # "chat" or "small"
    prompt: str
    max_tokens: int = 100

@app.post("/generate")
async def gen(req: Req):
    if req.model not in ("chat", "small"):
        return {"error": "unknown model"}
    text = generate(
        models[req.model], models[f"{req.model}_tok"],
        prompt=req.prompt, max_tokens=req.max_tokens, verbose=False,
    )
    return {"text": text, "model": req.model}
비동기 요청 묶기(가벼운 방식)·python
# A thin batching pattern: collect prompts that arrive within a short window,
# process serially via mlx-lm. Real batched generation is more involved;
# this approximation is good enough when you control your traffic shape.
import asyncio
from collections import deque

class BatchCollector:
    def __init__(self, window_ms: int = 25):
        self.window = window_ms / 1000.0
        self.queue: deque = deque()
        self.lock = asyncio.Lock()

    async def submit(self, prompt: str):
        future: asyncio.Future = asyncio.get_event_loop().create_future()
        async with self.lock:
            self.queue.append((prompt, future))
        await asyncio.sleep(self.window)
        # In a full implementation, drain the queue and process as a batch.
        # The minimum sketch: serial inference, returning each result.
        return await future

External links

Exercise

이 레슨의 여러 모델 FastAPI 개요를 로컬에서 실행해. 서로 다른 프롬프트 두 개를 보내 하나는 chat, 다른 하나는 small로 전달하고 요청별 지연 시간을 확인해. 작은 모델이 눈에 띄게 빨라야 해. 활동 모니터나 vm_stat로 두 모델이 모두 메모리에 상주하는지도 확인해. 여러 모델의 추가 비용과 그 비용이 작업 흐름에 가치가 있는지 두 문장으로 적어.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.