분리형 인코더

분리형 인코더 (Disaggregated Encoder)

분리형(disaggregated) 인코더는 멀티모달 LLM의 비전 인코더 단계를 프리필/디코더 단계와 별도 프로세스로 실행합니다. 두 단계를 독립적인 vLLM 인스턴스로 배포하면 세 가지 실질적인 이점이 생깁니다.

출처: 문서

설계 문서: https://docs.google.com/document/d/1aed8KtC6XkXtdoV87pWT0a8OJlZ-CpnuLLzmR8l9BAE

본문

1. 동기 (Motivation)

1. 독립적이고 세밀한 스케일링 (Independent, fine-grained scaling)

  • 비전 인코더는 가볍지만, 언어 모델은 그보다 수십 배 더 큽니다.
  • 언어 모델을 병렬화해도 인코더 플릿(fleet)에는 영향을 주지 않습니다.
  • 인코더 노드는 독립적으로 추가하거나 제거할 수 있습니다.

2. 낮은 첫 토큰 시간 (Lower time-to-first-token, TTFT)

  • 언어 전용 요청은 비전 인코더를 완전히 우회합니다.
  • 인코더 출력은 필요한 어텐션 레이어에만 주입되므로, 프리필 핵심 경로(critical path)가 짧아집니다.

3. 크로스-프로세스 재사용과 캐싱 (Cross-process reuse and caching)

  • 프로세스 내(in-process) 인코더는 재사용을 단일 워커로 한정합니다.
  • 원격·공유 캐시를 쓰면 어떤 워커든 기존 임베딩을 가져와 중복 연산을 없앨 수 있습니다.

2. 사용 예제 (Usage Example)

ExampleConnector

다음 스크립트들은 ExampleConnector로 전체 워크플로를 보여줍니다.

  • Encoder 1 + PD 1: examples/disaggregated/disaggregated_encoder/disagg_1e1pd_example.sh
  • Encoder 1 + Prefill 1 + Decode 1: examples/disaggregated/disaggregated_encoder/disagg_1e1p1d_example.sh

ECMooncakeConnector

ECMooncakeConnector는 Mooncake TransferEngine을 이용해 인코더 출력을 전송합니다. Encoder 1 + PD 1을 완전히 갖춘 설정(producer·consumer --ec-transfer-config와 프록시 구성 포함)은 Mooncake 통합 예제를 참고하세요.

vLLM과 Mooncake가 설치되어 있다면 저장소 루트에서 다음 예제를 실행합니다.

GPU_E=0 GPU_PD=1 MOONCAKE_EC_PROTOCOL=tcp \
    bash tests/v1/ec_connector/integration/run_epd_mooncake_ec_full_pipeline.sh

이 스크립트는 기본적으로 Qwen/Qwen2.5-VL-3B-Instruct를 사용하며 단일 GPU 베이스라인을 실행하고, 분리형 출력을 그 베이스라인과 비교합니다. 다른 모델을 쓰려면 MODEL을, 지원 하드웨어에서 RDMA를 쓰려면 MOONCAKE_EC_PROTOCOL=rdma를 설정하세요. 추가 구성 옵션은 스크립트를 참고하세요.

오디오 입력 (Audio inputs)

Python EPD 프록시는 Qwen2-Audio, AudioFlamingo3, Ultravox, Qwen2.5-Omni, Qwen3-Omni에 대해 /v1/chat/completions의 오디오를 메타데이터 전용 참조로 다시 작성합니다. 인코더는 각 오디오의 플레이스홀더 토큰 수(audio_num_tokens)를 모델이 필요로 하는 feature 길이와 함께 게시하고, consumer는 이를 이용해 오디오 플레이스홀더를 재구성하며 EC 커넥터로 임베딩을 받아 오디오 전처리를 반복하지 않습니다.

이는 순수 오디오 입력만 다루며, 오디오 트랙이 포함된 비디오는 다루지 않습니다. 예제 프록시에 /v1/audio/transcriptions나 realtime 라우트를 추가하지도 않습니다.

3. 테스트 스크립트 (Test Script)

Mooncake P2P 전달을 유지하면서 선택적으로 크로스-인코더 출력 재사용을 하려면 Cross-encoder output reuse를 참고하세요.

tests/v1/ec_connector 디렉토리를 참고하세요.

4. 개발 (Development)

분리형 인코딩은 두 부분을 실행하는 것으로 구현됩니다.

  • Encoder 인스턴스 — 비전 인코딩을 수행하는 vLLM 인스턴스.
  • Prefill/Decode (PD) 인스턴스 — 언어 프리필과 디코드를 실행. disagg_encoder_example.sh(E->PD)처럼 단일 일반 인스턴스일 수도 있고, disagg_epd_example.sh(E->P->D)처럼 분리 인스턴스일 수도 있습니다.

커넥터가 인코더 인스턴스에서 PD 인스턴스로 인코더 캐시(EC) 임베딩을 전송합니다. 관련 코드는 모두 vllm/distributed/ec_transfer 아래에 있습니다.

핵심 추상화 (Key abstractions)

  • ECConnector — 인코더가 만든 EC 캐시를 가져오는 인터페이스.
    • Scheduler 역할 — 캐시 존재 여부를 확인하고 로드를 스케줄링.
    • Worker 역할 — 임베딩을 메모리에 로드.

다음 그림은 분리형 인코더 흐름을 보여줍니다.

PD 분리 부분에서 Prefill 인스턴스는 위 분리형 인코더 흐름과 정확히 같은 방식으로 캐시를 받습니다. Prefill 인스턴스는 1단계(prefill → 1 토큰 출력)를 실행한 뒤 KV 캐시를 Decode 인스턴스로 전송해 나머지를 실행합니다. KV 전송 부분은 PD 인스턴스 실행 이후에만 이루어집니다.

docs/features/disagg_prefill.md는 분리형 프리필(v0)에 대한 간단한 개념을 보여줍니다.

우리는 vllm/distributed/kv_transfer/kv_connector/v1/nixl/NixlConnector로 예제 설정을 만들고, P와 D 사이의 KV 전송을 위해 tests/v1/kv_connector/nixl_integration/toy_proxy_server.py를 참고했습니다.

더 알아보기 (Learn more)