EPD 분리

EPD 분리 (EPD Disaggregation)

PD 분리가 추론 엔진에서 프리필과 디코드를 갈라놓는 것이라면, EPD(Encoder–Prefill–Decode) 분리는 VLM(비전-언어 모델) 추론에서 비전 인코딩까지 통째로 떼어내는 더 진보된 확장이에요. 비전이 섞인 이미지 위주 워크로드를 돌린다면 한번 살펴볼 만해요.

EPD 분리가 뭐고 왜 필요한가?

현대 VLM 추론에서 요청 실행은 자연스럽게 인코더(Encoder), 프리필(Prefill), 디코드(Decode) 라는 세 가지 단계로 나뉘어요. 인코더 단계는 비전 전처리와 ViT 기반 이미지 인코딩을 수행하는데, 계산은 엄청 집약적이지만 요청 초기화에만 필요해요. 프리필 단계는 전체 멀티모달 입력 시퀀스를 처리해 언어 모델의 Key-Value(KV) 캐시를 초기화하고, 디코드 단계는 자기회귀 토큰 생성을 위한 메모리 대역폭과 KV 캐시 접근이 지배해요.

기존 배포는 보통 이 단계들을 하나의 통합 실행 엔진에 모아두거나, 기껏해야 PD 분리를 적용해요. 하지만 그런 설계는 여전히 비전 인코딩과 언어 프리필을 밀접하게 묶어두기 때문에, 리소스 활용 비효율, 이미지 위주 워크로드의 제한된 확장성, 부하 상황에서의 최적이 아닌 스케줄링이 발생해요.

이 문제를 해결하려고 SGLang은 EPD 분리를 도입했어요. EPD는 비전 인코딩을 언어 처리에서 더 분리해서, 인코더 서버의 독립적 수평 확장, 멀티모달 요청의 개선된 로드 밸런싱, 그리고 기존 PD 분리와의 원활한 통합(완전히 분리된 3계층 추론 아키텍처)을 가능하게 해요.

사용법

--language-only로 언어 전용 모델을, --encoder-only로 인코더 전용 모델을 띄울 수 있어요. 언어 전용 모델을 띄울 때는 반드시 --encoder-urls로 인코더 서비스 엔드포인트를 추가로 지정해야 해요.

여러 인코더 전송 백엔드를 지원하는데, zmq_to_scheduler, zmq_to_tokenizer, mooncake(기본은 zmq_to_scheduler)가 있어요. 백엔드는 --encoder-transfer-backend로 선택할 수 있어요.

Mooncake와 함께하는 인코더 전송

--encoder-transfer-backend mooncake인코더 출력이 인코더·언어/프리필 서비스 간에 어떻게 전송되는지 제어해요. 인코더 전송 옵션이며, 전역 멀티모달 임베딩 캐시와는 독립적으로 사용할 수 있어요.

예시:

# encoder
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --encoder-only \
  --encoder-transfer-backend mooncake \
  --port 30000

# language-only 서버
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --language-only \
  --encoder-urls http://127.0.0.1:30000 \
  --encoder-transfer-backend mooncake \
  --port 30002

Mooncake와 함께하는 전역 멀티모달 임베딩 캐시

SGLang은 EPD 워크로드용으로 Mooncake 기반 전역 멀티모달 임베딩 캐시도 지원해요. 인코더 서버에서 활성화하면, 반복되는 이미지 입력이 비전 인코더를 다시 돌리는 대신 인스턴스 간에 이전에 계산된 ViT 임베딩을 재사용할 수 있어요.

이 기능은 다음 상황에서 유용해요:

  • 배포가 반복되거나 겹치는 이미지 입력을 서빙할 때,
  • 인코더 연산이 병목일 때,
  • 클러스터에 Mooncake가 이미 있을 때.

높은 수준에서, 인코더는 이미지 임베딩이 Mooncake에 이미 존재하는지 확인해요. 캐시 히트는 전역 저장소에서 프리페치되고, 미스는 정상적으로 인코딩된 뒤 백그라운드에서 캐시에 삽입돼요.

활성화하려면:

  • 다른 SGLang Mooncake 통합과 같은 방식으로 Mooncake를 설치·설정하고,
  • 인코더 서버에 --enable-mm-global-cache를 추가해요.

--enable-mm-global-cache멀티모달 임베딩을 전역 Mooncake 캐시에서 조회·저장할지 제어해요. 인코더 출력 전송만 제어하는 --encoder-transfer-backend와는 별개예요.

Mooncake 배포·설정 자세한 내용은 HiCache 모범 사례Mooncake 백엔드 README를 참고하세요.

예시:

# 공유 Mooncake 설정
export MOONCAKE_TE_META_DATA_SERVER="http://127.0.0.1:8080/metadata"
export MOONCAKE_MASTER="127.0.0.1:50051"
export MOONCAKE_PROTOCOL="rdma"
export MOONCAKE_GLOBAL_SEGMENT_SIZE="4gb"

# 전역 멀티모달 캐시가 활성화된 encoder
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --encoder-only \
  --enable-mm-global-cache \
  --port 30000

# language-only 서버
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --language-only \
  --encoder-urls http://127.0.0.1:30000 \
  --port 30002

참고:

  • 이 캐시는 멀티모달 인코더 임베딩용이지, 언어 모델 KV 캐시가 아니에요.
  • 이 기능은 현재 Mooncake를 공유 backing store로 사용해요.
  • 어떤 --encoder-transfer-backend를 쓰든 활성화할 수 있어요.
  • 요청·인스턴스 간 같은 이미지가 나타날 가능성이 높은 EPD 또는 인코더-분리 VLM 배포에서 가장 관련이 높아요.

Qwen VL

  • EP 분리
# encoder 0
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --encoder-only \
  --encoder-transfer-backend zmq_to_scheduler \
  --port 30000
# encoder 1
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --encoder-only \
  --encoder-transfer-backend zmq_to_scheduler \
  --port 30001
# language-only 서버
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --language-only \
  --encoder-urls http://127.0.0.1:30000 http://127.0.0.1:30001 \
  --encoder-transfer-backend zmq_to_scheduler \
  --port 30002
  • EPD 분리
# encoder 0
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --encoder-only \
  --encoder-transfer-backend zmq_to_scheduler \
  --port 30000
# encoder 1
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --encoder-only \
  --encoder-transfer-backend zmq_to_scheduler \
  --port 30001
# prefill 0
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --disaggregation-mode prefill \
  --language-only \
  --encoder-urls http://127.0.0.1:30000 http://127.0.0.1:30001 \
  --encoder-transfer-backend zmq_to_scheduler \
  --port 30002
# decode 0
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --disaggregation-mode decode \
  --port 30003
# router
python -m sglang_router.launch_router \
  --pd-disaggregation \
  --prefill http://$PREFILL_HOST:30002 \
  --decode http://$DECODE_HOST:30003 \
  --port 8000

gRPC 인코더 (EPD)

인코더를 gRPC 서버로, prefill/decode는 HTTP로 실행할 수 있어요. gRPC 인코더를 쓸 때는 prefill 프로세스에 SGLANG_ENCODER_MM_RECEIVER_MODE=grpc를 설정해 gRPC 리시버를 쓰게 해요.

# gRPC encoder
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --encoder-only \
  --grpc-mode \
  --encoder-transfer-backend zmq_to_scheduler \
  --port 30000

# prefill (HTTP) - gRPC 리시버를 쓰라고 지시
SGLANG_ENCODER_MM_RECEIVER_MODE=grpc \
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --disaggregation-mode prefill \
  --language-only \
  --encoder-urls grpc://127.0.0.1:30000 \
  --encoder-transfer-backend zmq_to_scheduler \
  --port 30002

# decode (HTTP)
python -m sglang.launch_server \
  --model-path Qwen/Qwen3-VL-8B-Instruct \
  --disaggregation-mode decode \
  --port 30003

# router
python -m sglang_router.launch_router \
  --pd-disaggregation \
  --prefill http://$PREFILL_HOST:30002 \
  --decode http://$DECODE_HOST:30003 \
  --port 8000

더 알아보기 (Learn more)