전문가 병렬 배포

전문가 병렬 배포 (Expert Parallel Deployment)

vLLM은 Expert Parallelism(EP)을 지원합니다. EP는 MoE(Mixture-of-Experts) 모델의 전문가(Expert)를 별도의 GPU에 배포해 전체적으로 로컬리티·효율·처리량을 높일 수 있게 합니다. EP는 보통 데이터 병렬(DP)과 함께 사용되며, DP는 EP 없이 독립적으로 쓸 수 있지만 EP는 DP와 결합할 때 더 효율적입니다.

출처: 문서

본문

vLLM은 EP를 지원하며, MoE 모델의 전문가를 별도 GPU에 배포해 로컬리티·효율·처리량을 전반적으로 높입니다. EP는 보통 DP와 결합해 씁니다. DP는 EP 없이 독립적으로도 쓸 수 있지만, EP는 DP와 함께 쓸 때 더 효율적입니다. 데이터 병렬에 대한 자세한 내용은 여기에서 읽을 수 있습니다.

사전 준비 (Prerequisites)

EP를 사용하려면 필요한 의존성을 설치해야 합니다. 향후 이를 더 쉽게 만들기 위해 적극적으로 작업 중입니다:

  1. DeepEP 설치: vLLM의 EP 커널 가이드 여기를 따라 호스트 환경을 설정하세요.
  2. DeepGEMM 라이브러리 설치: 공식 지침을 따르세요.
  3. 분리(disaggregated) 서빙용: install_gdrcopy.sh 스크립트를 실행해 gdrcopy를 설치하세요(예: install_gdrcopy.sh "${GDRCOPY_OS_VERSION}" "12.8" "x64"). 사용 가능한 OS 버전은 여기에서 찾을 수 있습니다.

NCCL 버전 (CUDA 13+)

deepep_v2 백엔드는 NCCL >= 2.30.4를 요구합니다. PyTorch는 더 오래된 NCCL을 제공하므로, DeepEP를 빌드/실행하기 전에 반드시 업그레이드해야 합니다. 지침은 EP kernels 가이드를 참고하세요.

백엔드 선택 가이드 (Backend Selection Guide)

vLLM은 EP용 여러 통신 백엔드를 제공합니다. --all2all-backend로 선택합니다:

백엔드 사용 사례 특징 적합한 용도
allgather_reducescatter 기본 백엔드 allgather/reducescatter 프리미티브를 쓰는 표준 all2all 일반 목적, 모든 EP+DP 구성에서 동작
deepep_high_throughput 멀티 노드 prefill 연속 레이아웃의 Grouped GEMM, prefill에 최적화 prefill 중심 워크로드, 고처리량 시나리오
deepep_low_latency 멀티 노드 decode CUDA graph 지원, masked 레이아웃, decode에 최적화 decode 중심 워크로드, 저지연 시나리오
flashinfer_nvlink_one_sided MNNVL 시스템 멀티 노드 NVLink용 FlashInfer 단방향 A2A 전략 고처리량 워크로드
flashinfer_nvlink_two_sided MNNVL 시스템 멀티 노드 NVLink용 FlashInfer 양방향 A2A 전략 노드 간 NVLink가 있는 시스템

단일 노드 배포 (Single Node Deployment)

구성

--enable-expert-parallel 플래그를 설정해 EP를 활성화합니다. EP 크기는 자동으로 계산됩니다:

EP_SIZE = TP_SIZE × DP_SIZE

여기서:

  • TP_SIZE: Tensor parallel 크기
  • DP_SIZE: Data parallel 크기
  • EP_SIZE: Expert parallel 크기 (자동 계산)

EP 활성화 시 레이어 동작 (Layer Behavior with EP Enabled)

EP를 켜면 MoE 모델의 서로 다른 레이어가 다르게 동작합니다:

레이어 유형 동작 사용 병렬화
전문가(MoE) 레이어 모든 EP 랭크에 걸쳐 샤딩 크기 TP × DP의 EP
어텐션 레이어 TP 크기에 따라 다름 아래 참고

어텐션 레이어 병렬화:

  • TP = 1일 때: 어텐션 가중치가 모든 DP 랭크에 복제됩니다(데이터 병렬)
  • TP > 1일 때: 각 DP 그룹 내 TP 랭크에 걸쳐 tensor parallelism으로 어텐션 가중치가 샤딩됩니다

예를 들어 TP=2, DP=4(총 8 GPU)라면:

  • 전문가 레이어는 크기 8의 EP 그룹을 형성하고, 전문가가 모든 GPU에 분산됩니다
  • 어텐션 레이어는 4개 DP 그룹 각각에서 TP=2를 사용합니다

데이터 병렬 배포와의 핵심 차이

--enable-expert-parallel이 없으면 MoE 레이어는 밀집 모델과 유사하게 tensor parallelism(크기 TP × DP의 TP 그룹 형성)을 사용합니다. EP를 켜면 전문가 레이어가 expert parallelism으로 전환되어 MoE 모델에 더 나은 효율과 로컬리티를 제공할 수 있습니다.

예시 명령

다음 명령은 DeepSeek-V3-0324 모델을 1-way tensor parallel, 8-way(어텐션) 데이터 병렬, 8-way expert parallel로 서빙합니다. 어텐션 가중치는 모든 GPU에 복제되고, 전문가 가중치는 GPU에 split됩니다. 8개 GPU가 있는 H200(또는 H20) 노드에서 동작합니다. H100이라면 더 작은 모델을 서빙하거나 멀티 노드 배포 섹션을 참고하세요.

# Single node EP deployment
vllm serve deepseek-ai/DeepSeek-V3-0324 \
    --tensor-parallel-size 1 \       # Tensor parallelism across 1 GPU
    --data-parallel-size 8 \         # Data parallelism across 8 processes
    --enable-expert-parallel         # Enable expert parallelism

멀티 노드 배포 (Multi-Node Deployment)

멀티 노드 배포에는 두 모드 중 하나(위 Backend Selection Guide 참고)로 DeepEP 통신 커널을 사용합니다.

배포 단계

  1. 노드당 명령 하나 실행 — 각 노드는 자체 실행 명령이 필요합니다
  2. 네트워킹 구성 — 올바른 IP 주소와 포트를 구성하세요
  3. 노드 역할 설정 — 첫 노드가 요청을 처리하고, 추가 노드는 headless 모드로 실행됩니다

예시: 2-노드 배포

다음 예시는 deepep_low_latency 모드로 DeepSeek-V3-0324를 2개 노드에 배포합니다:

# Node 1 (Primary - handles incoming requests)
vllm serve deepseek-ai/DeepSeek-V3-0324 \
    --all2all-backend deepep_low_latency \
    --tensor-parallel-size 1 \               # TP size per node
    --enable-expert-parallel \               # Enable EP
    --data-parallel-size 16 \                # Total DP size across all nodes
    --data-parallel-size-local 8 \           # Local DP size on this node (8 GPUs per node)
    --data-parallel-address 192.168.1.100 \  # Replace with actual IP of Node 1
    --data-parallel-rpc-port 13345 \         # RPC communication port, can be any port as long as reachable by all nodes
    --api-server-count=8                     # Number of API servers for load handling (scaling this out to # local ranks is recommended)

# Node 2 (Secondary - headless mode, no API server)
vllm serve deepseek-ai/DeepSeek-V3-0324 \
    --all2all-backend deepep_low_latency \
    --tensor-parallel-size 1 \               # TP size per node
    --enable-expert-parallel \               # Enable EP
    --data-parallel-size 16 \                # Total DP size across all nodes
    --data-parallel-size-local 8 \           # Local DP size on this node
    --data-parallel-start-rank 8 \           # Starting rank offset for this node
    --data-parallel-address 192.168.1.100 \  # IP of primary node (Node 1)
    --data-parallel-rpc-port 13345 \         # Same RPC port as primary
    --headless                               # No API server, worker only

핵심 구성 참고사항

  • Headless 모드: 보조 노드는 --headless 플래그로 실행되며, 모든 클라이언트 요청을 기본 노드가 처리합니다
  • 랭크 계산: --data-parallel-start-rank는 이전 노드들의 누적 로컬 DP 크기와 같아야 합니다
  • 부하 확장: 기본 노드에서 --api-server-count를 조정해 더 높은 요청 부하를 처리하세요

네트워크 구성

InfiniBand 클러스터

InfiniBand 네트워크 클러스터에서는 초기화 hang을 방지하기 위해 이 환경 변수를 설정하세요:

export GLOO_SOCKET_IFNAME=eth0

이렇게 하면 torch distributed 그룹 발견이 초기 설정에 InfiniBand 대신 Ethernet을 사용합니다.

전문가 병렬 로드밸런서 (EPLB)

MoE 모델은 보통 각 전문가가 비슷한 토큰 수를 받도록 학습되지만, 실제로는 전문가 간 토큰 분포가 크게 치우칠 수 있습니다. vLLM은 Expert Parallel Load Balancer(EPLB)를 제공해 EP 랭크 간 전문가 매핑을 재분배하여 전문가 간 부하를 고르게 합니다.

구성

--enable-eplb 플래그로 활성화합니다.

활성화하면 vLLM은 매 포워드 패스마다 부하 통계를 수집하고 주기적으로 전문가 분포를 재조정합니다.

EPLB 파라미터

--eplb-config 인자로 구성하며, JSON 문자열을 받습니다. 사용 가능한 키와 설명:

파라미터 설명 기본값
window_size 재조정 결정을 위해 추적할 엔진 스텝 수 1000
step_interval 재조정 빈도 (매 N 엔진 스텝) 3000
log_balancedness 밸런스드니스 지표 기록 (전문가당 평균 토큰 ÷ 전문가당 최대 토큰) false
num_redundant_experts 균등 분배를 넘어 EP 랭크당 추가되는 전역 전문가 수 0
use_async 지연 오버헤드 감소를 위한 non-blocking EPLB 사용 true
policy 전문가 병렬 로드밸런싱 정책 유형 "default"
communicator 전문가 가중치 전송 백엔드: "torch_nccl", "torch_gloo", "pynccl", "nixl", 또는 null(자동) null

예를 들어:

vllm serve Qwen/Qwen3-30B-A3B \
  --enable-eplb \
  --eplb-config '{"window_size":1000,"step_interval":3000,"num_redundant_experts":2,"log_balancedness":true}'

JSON 대신 개별 인자가 더 편한가요?

vllm serve Qwen/Qwen3-30B-A3B \
        --enable-eplb \
        --eplb-config.window_size 1000 \
        --eplb-config.step_interval 3000 \
        --eplb-config.num_redundant_experts 2 \
        --eplb-config.log_balancedness true

전문가 분포 공식

  • 기본: 각 EP 랭크는 NUM_TOTAL_EXPERTS ÷ NUM_EP_RANKS개 전문가를 가짐
  • 리던던시 포함: 각 EP 랭크는 (NUM_TOTAL_EXPERTS + NUM_REDUNDANT_EXPERTS) ÷ NUM_EP_RANKS개 전문가를 가짐

메모리 오버헤드

EPLB는 GPU 메모리에 들어가야 하는 리던던트 전문가를 사용합니다. 즉 메모리 제약 환경이나 KV cache 공간이 귀중한 환경에는 EPLB가 맞지 않을 수 있습니다.

이 오버헤드는 NUM_MOE_LAYERS * BYTES_PER_EXPERT * (NUM_TOTAL_EXPERTS + NUM_REDUNDANT_EXPERTS) ÷ NUM_EP_RANKS와 같습니다. DeepSeekV3의 경우 EP 랭크당 리던던트 전문가 1개당 약 2.4 GB입니다.

예시 명령

EPLB를 활성화한 단일 노드 배포:

# Single node with EPLB load balancing
vllm serve deepseek-ai/DeepSeek-V3-0324 \
    --tensor-parallel-size 1 \       # Tensor parallelism
    --data-parallel-size 8 \         # Data parallelism
    --enable-expert-parallel \       # Enable EP
    --enable-eplb \                  # Enable load balancer
    --eplb-config '{"window_size":1000,"step_interval":3000,"num_redundant_experts":2,"log_balancedness":true}'

멀티 노드 배포에서는 각 노드의 명령에 EPLB 플래그를 추가하세요. 대규모 사용에서는 --eplb-config '{"num_redundant_experts":32}'로 32를 설정해 인기 전문가가 항상 사용 가능하도록 하는 것을 권장합니다.

고급 구성 (Advanced Configuration)

성능 최적화

  • DeepEP 커널: high_throughputlow_latency 커널은 분리(disaggregated) 서빙에 최적화되어 있어 혼합 워크로드에서는 성능이 좋지 않을 수 있습니다
  • Dual Batch Overlap: --enable-dbo로 all-to-all 통신과 컴퓨팅을 겹칩니다. 자세한 내용은 Dual Batch Overlap 참고
  • 비동기 스케줄링 (실험적): --async-scheduling으로 스케줄링을 모델 실행과 겹쳐 보세요

문제 해결

  • non-zero status: 7 cannot register cq buf: Infiniband/RoCE 사용 시 호스트 VM과 파드의 ulimit -l이 "unlimited"인지 확인하세요
  • init failed for transport: IBGDA: InfiniBand GDA 커널 모듈이 없습니다. 각 GPU 노드에서 tools/ep_kernels/configure_system_drivers.sh를 실행하고 재부팅하세요. NVSHMEM API called before NVSHMEM initialization has completed 오류도 해결합니다
  • NVSHMEM 피어 연결 끊김: 보통 네트워킹 설정 오류입니다. Kubernetes로 배포한다면 모든 파드가 Infiniband 접근을 위해 hostNetwork: true, securityContext.privileged: true로 실행되는지 확인하세요

벤치마킹

  • 시뮬레이터 플래그 VLLM_MOE_ROUTING_SIMULATION_STRATEGY=uniform_randomVLLM_RANDOMIZE_DP_DUMMY_INPUTS=1을 사용해 토큰 라우팅이 EP 랭크 간 균등하게 되게 하세요.

분리 서빙 (Disaggregated Serving, Prefill/Decode 분리)

첫 토큰까지의 시간과 inter-token 지연에 대한 엄격한 SLA 보장이 필요한 프로덕션 배포에서, disaggregated serving은 prefill과 decode 연산을 독립적으로 확장할 수 있게 합니다.

아키텍처 개요

  • Prefill 인스턴스: 최적의 prefill 성능을 위해 deepep_high_throughput 백엔드 사용
  • Decode 인스턴스: 최소 decode 지연을 위해 deepep_low_latency 백엔드 사용
  • KV Cache 전송: NIXL 또는 다른 KV 커넥터로 인스턴스를 연결

설정 단계

  1. gdrcopy/ucx/nixl 설치: 최대 성능을 위해 install_gdrcopy.sh 스크립트로 gdrcopy를 설치하세요(예: install_gdrcopy.sh "${GDRCOPY_OS_VERSION}" "12.8" "x64"). OS 버전은 여기에서 찾을 수 있습니다. gdrcopy가 없어도 일반 pip install nixl로 동작은 하지만 성능은 더 낮습니다. nixlucx는 pip 의존성으로 설치됩니다. non-cuda 플랫폼에서 non-cuda UCX 빌드로 nixl을 설치하려면 install_nixl_from_source_ubuntu.py를 실행하세요.
  2. 양쪽 인스턴스 구성: prefill과 decode 인스턴스 모두에 이 플래그를 추가하세요 --kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_both"}'. 하나 또는 여러 NIXL_Backend를 지정할 수도 있습니다. 예: --kv-transfer-config '{"kv_connector":"NixlConnector","kv_role":"kv_both", "kv_connector_extra_config":{"backends":["UCX", "GDS"]}}'
  3. 클라이언트 오케스트레이션: 아래 클라이언트 측 스크립트로 prefill/decode 연산을 조정하세요. 라우팅 솔루션을 적극적으로 작업 중입니다.

클라이언트 오케스트레이션 예시

from openai import OpenAI
import uuid

try:
    # 1: Set up clients for prefill and decode instances
    openai_api_key = "EMPTY"  # vLLM doesn't require a real API key

    # Replace these IP addresses with your actual instance addresses
    prefill_client = OpenAI(
        api_key=openai_api_key,
        base_url="http://192.168.1.100:8000/v1",  # Prefill instance URL
    )
    decode_client = OpenAI(
        api_key=openai_api_key,
        base_url="http://192.168.1.101:8001/v1",  # Decode instance URL  
    )

    # Get model name from prefill instance
    models = prefill_client.models.list()
    model = models.data[0].id
    print(f"Using model: {model}")

    # 2: Prefill Phase
    # Generate unique request ID to link prefill and decode operations
    request_id = str(uuid.uuid4())
    print(f"Request ID: {request_id}")

    prefill_response = prefill_client.completions.create(
        model=model,
        # Prompt must exceed vLLM's block size (16 tokens) for PD to work
        prompt="Write a detailed explanation of Paged Attention for Transformers works including the management of KV cache for multi-turn conversations",
        max_tokens=1,  # Force prefill-only operation
        extra_body={
            "kv_transfer_params": {
                "do_remote_decode": True,     # Enable remote decode
                "do_remote_prefill": False,   # This is the prefill instance
                "remote_engine_id": None,     # Will be populated by vLLM
                "remote_block_ids": None,     # Will be populated by vLLM
                "remote_host": None,          # Will be populated by vLLM
                "remote_port": None,          # Will be populated by vLLM
            }
        },
        extra_headers={"X-Request-Id": request_id},
    )

    print("-" * 50)
    print("✓ Prefill completed successfully")
    print(f"Prefill response: {prefill_response.choices[0].text}")

    # 3: Decode Phase
    # Transfer KV cache parameters from prefill to decode instance
    decode_response = decode_client.completions.create(
        model=model,
        prompt="This prompt is ignored during decode",  # Original prompt not needed
        max_tokens=150,  # Generate up to 150 tokens
        extra_body={
            "kv_transfer_params": prefill_response.kv_transfer_params  # Pass KV cache info
        },
        extra_headers={"X-Request-Id": request_id},  # Same request ID
    )

    print("-" * 50)
    print("✓ Decode completed successfully")
    print(f"Final response: {decode_response.choices[0].text}")

except Exception as e:
    print(f"❌ Error during disaggregated serving: {e}")
    print("Check that both prefill and decode instances are running and accessible")

벤치마킹

  • 분리 서빙의 decode 배포를 시뮬레이션하려면 vllm serve 호출에 --kv-transfer-config '{"kv_connector":"DecodeBenchConnector","kv_role":"kv_both"}'를 전달하세요. 커넥터가 KV cache를 무작위 값으로 채워 decode를 단독으로 프로파일링하게 합니다
  • CUDAGraph 캡처: --compilation_config '{"cudagraph_mode": "FULL_DECODE_ONLY"}'로 decode 전용 CUDA graph 캡처를 활성화하고 KV cache를 절약하세요.

더 알아보기 (Learn more)