긴 컨텍스트용 파이프라인 병렬화

긴 컨텍스트용 파이프라인 병렬화 (Pipeline Parallelism for Long Context)

파이프라인 병렬화(pipeline parallelism)로 긴 컨텍스트 입력의 TTFT를 줄이는 방법을 설명합니다. 동적 청크 프리필(dynamic chunked prefill)과 비동기 P2P 통신 기반의 구현, 그리고 실제 튜닝 가이드를 다뤄요.

출처: 문서

본문

왜 파이프라인 병렬화인가 (Why Pipeline Parallelism?)

LLM이 조 단위 파라미터 아키텍처와 "무한" 컨텍스트 윈도우로 확장하면서, 기반 서빙 인프라는 더 세분화된 크로스 노드 병렬화 전략으로 진화해야 합니다. KV 캐시 기법은 중복 연산을 효과적으로 완화하지만, 매우 긴 시퀀스에서 큰 초기 Input Token Length (ITL)에 내재된 금지적인 Time to First Token (TTFT)을 우회할 수는 없습니다. Tensor Parallelism (TP)은 인트라 노드 스케일링의 관례적 접근이지만 멀티 노드 배포에서 통신 병목을 자주 만납니다. 반면 파이프라인 병렬화는 각 파이프라인 스테이지 경계에서만 크로스 노드 통신을 요구하므로, 큰 TP보다 더 나은 computation-communication overlap을 달성할 수 있습니다. 따라서 처리량 향상을 위한 유망한 병렬화 전략이기도 합니다.

자세한 분석은 이 blog에서 볼 수 있습니다.

비동기 통신 기반 구현 리팩토링 (Implementation Refactoring based on Async Communication)

Dynamic Chunked Prefill을 사용하면 파이프라인 병렬화가 긴 컨텍스트 입력의 TTFT를 줄일 잠재력이 있습니다. 각 요청의 입력 토큰은 chunked prefill 크기를 넘지 않는 여러 청크로 분할될 수 있습니다. 같은 요청의 서로 다른 청크는 서로 다른 노드에서 동시에 처리될 수 있어 처리량을 병렬화하고 TTFT를 줄입니다. SGLang은 한동안 Pipeline Parallelism(#5724)을 지원했고 PD Disaggregation 기능(#8846)과 호환되도록 만들었지만, 구현이 완벽하지 않아 성능 개선 여지가 상당했습니다.

이 성능 위험을 없애기 위해, SGLang은 non-blocking 비동기 피어투피어(P2P) 통신으로 Micro-batching Event Loop를 구현해 GPU 연산을 CPU 메타데이터 처리 및 PP 통신과 겹칩니다. 이는 하나의 micro-batch가 GPU에서 계산되는 동안 다음 것도 이미 준비되고 효과적으로 위치로 옮겨져, 파이프라인이 가능한 한 포화 상태를 유지하게 보장합니다. 이 접근은 #7979에서 처음 제안되었고 재설계되어 #11852에 포함되었습니다.

구현의 핵심 메커니즘은 다음과 같습니다:

  • 이벤트 루프의 동기/비동기 로직 분리 (Decoupled Sync/Async Logic in the Event Loop): 스케줄러는 _pp_send_pyobj_to_next_stage에서 async_send를 사용합니다. 전송 완료를 기다리는 대신 P2PWork 핸들을 반환합니다. 실제 동기화(P2PWork.work.wait())는 _pp_commit_comm_work가 호출될 때까지 지연되어, 데이터가 전송되는 동안 CPU가 다른 작업(다음 배치 스케줄링, 메타데이터 처리 등)을 수행할 수 있게 합니다.
  • 멀티 스트림 실행 (Multi-Stream Execution): 동기화 스트림 역할을 하는 메인 default_stream 외에도, SGLang은 전용 forward_streamcopy_stream을 사용해 forward pass GPU 계산과 Data-to-Host (D2H) 메모리 전송을 각각 더 잘 겹칩니다. _pp_launch_batch가 현재 스테이지에서 GPU로 현재 micro-batch를 실행하는 동안, CPU는 _pp_process_batch_result로 이전 micro-batch의 결과를 처리합니다.

Dynamic Chunking에 대한 안내 (Guidance about Dynamic Chunking)

왜 Dynamic Chunking인가 (Why Dynamic Chunking)

고정 크기의 chunked prefill은 특히 pp size가 클 때 파이프라인에 버블(bubble)을 만들 수 있습니다. 이 현상의 주된 이유는 각 청크 크기가 동일해도(Transformer 구조 때문) 모델의 실행 시간이 균일하지 않기 때문입니다. 프리픽스 시퀀스 길이가 길수록 청크의 실행 시간이 길어집니다. 그리고 이 버블은 다음 스테이지로 전파되어 더 큰 pp 랭크의 scale efficiency를 크게 저하시킵니다.

이 문제를 해결하기 위해 SGLang은 다음 조건을 만족하도록 다음 청크의 최적 크기를 예측하는 dynamic chunking 메커니즘을 도입합니다:

Runtime(L + Next Chunk Size) - Runtime(L) = Runtime(Initial Chunk Size)

여기서 L은 Prefix Sequence Length를 뜻합니다. 다양한 ITL의 일련의 요청을 프로파일링해 누적 런타임을 시퀀스 길이의 이차 함수로 모델링합니다. 이 모델을 사용해 주어진 프리픽스 길이 L에 대한 최적 다음 청크 크기를 풉니다. Attention 메커니즘의 계산 복잡도가 L에 따라 확장되므로, 파이프라인 스테이지 간 정렬된 청크 실행 시간을 유지하기 위해 L이 커질수록 다음 청크 크기는 점진적으로 줄어듭니다.

이 방법을 기반으로 스케줄러는 스테이지 비정렬로 인한 버블을 최소화하기 위해 런타임 중 청크 크기를 예측·동적으로 줄일 수 있습니다. 스케줄러는 원시 예측값을 사용하지 않는다는 점에 주의하세요. 효율적인 KVCache 메모리 관리와 하드웨어 실행 효율과의 친화성을 위해, 값은 max(--page-size, 64)의 가장 가까운 배수로 아래로 정렬됩니다.

청크 프리필 크기와 스무딩 팩터 (Chunked Prefill Size and Smoothing Factor)

--enable-dynamic-chunking이 활성화되면 시퀀스의 각 청크 크기는 초기 청크 길이의 예상 런타임을 기반으로 다음 청크 크기를 예측하는 이차 모델에 따라 동적으로 결정됩니다. 이 경우 --chunked-prefill-size로 초기 청크 크기를 설정합니다. dynamic chunking 모드로 전환하면 초기 청크 크기(--chunked-prefill-size)는 원래 chunked prefill 크기와 비슷한 더 큰 값으로 설정되어야 청크가 너무 많아지지 않습니다.

SGLANG_DYNAMIC_CHUNKING_SMOOTH_FACTOR 는 dynamic chunking 알고리즘의 스무딩 팩터를 제어하는 환경 변수로, 기본값은 0.75입니다. 이는 prefill 단계에서 청크 크기가 얼마나 변할 수 있는지를 결정합니다. 값이 클수록 더 공격적인 청크 크기 변경을 의미하며, 더 나은 성능으로 이어질 수 있지만 더 큰 청크 크기 변경(끝의 청크 크기가 매우 작아져 성능 저하로 이어질 수 있음)과 더 많은 총 청크 수도 초래합니다. 1로 설정하면 청크 크기는 다음 청크 크기를 예측하는 앞서 언급한 이차 모델에 따라 엄격히 조정됩니다. 값이 작을수록 보수적인 청크 크기 변경을 의미하며, 더 작은 청크 크기 변경과 더 적은 총 청크 수로 이어질 수 있습니다. 0으로 설정하면 청크 크기가 동적으로 조정되지 않아 고정 chunked prefill 크기의 전통적인 방식과 동일합니다.

하드웨어, 모델, 대상 워크로드의 변동 때문에 정적 구성이 모든 시나리오에서 최적이 되는 경우는 드뭅니다. 따라서 dynamic chunking 모드로 전환할 때 피크 성능을 달성하려면 어느 정도의 하이퍼파라미터 튜닝이 필요합니다.

Dynamic Chunked Prefill 튜닝 가이드 (Tuning Guidance)

  • 1단계 — 대상 PP 크기에 대한 최적 고정 chunked prefill 크기를 반복해 찾기: 대상 ITL에 따라 PP 크기마다 최적의 chunked prefill 크기가 다를 수 있습니다. 따라서 사용자는 스케일링에 사용 가능한 자원에 따라 베이스라인을 얻기 위해 반복해야 합니다.
  • 2단계 — 동적 청킹의 초기 청크 크기 선택 (Initial Chunk Size Selection): 초기 크기를 최적 고정 chunked prefill 크기의 2배 또는 3배로 설정하세요. 이는 총 청크 수를 줄이고 "tail chunk"가 하드웨어를 충분히 활용하지 못하게 방지합니다. 매우 큰 Input Token Length (ITL)에 대한 효율을 유지하기 위해, 동적 예측기는 후속 청크가 이 초기 크기의 최소 1/4이 되도록 자동 보장합니다. 또한 이런 경우엔 더 큰 초기 청크 크기(예: 최적 고정 chunked prefill 크기의 4배)를 사용하는 것을 권장합니다.
  • 3단계 — 스무딩 팩터 조정 (Smooth Factor Adjustment): 이 팩터는 청크 크기가 이차 성능 피팅 모델이 주는 예측을 얼마나 엄격히 따르는지 제어합니다.
    • 1.0: 모델을 엄격히 따름.
    • 0.6 – 0.85 (권장): 동적 스케일링과 하드웨어 안정성 사이의 최상의 균형을 위한 전형적 범위. 실험을 통해 0.6~0.85 범위가 dynamic chunking에 가장 좋은 성능을 주는 것을 발견했습니다.
    • 0: 동적 조정을 비활성화, 전통적인 고정 크기 청킹으로 되돌림.
  • 또 다른 작은 최적화 팁: 레이어가 랭크 전체에 균등하게 나눠지지 않을 때 더 큰 파티션을 더 높은 PP 랭크에 두세요. 더 큰 PP 랭크가 이전 스테이지의 결과를 기다릴 때 GPU 활용률을 높여 더 높은 PP 랭크에서의 버블을 줄일 수 있습니다. DeepSeek-V3.1을 예로 들면, SGLANG_PP_LAYER_PARTITION=15,15,15,16이 보통 16,15,15,15보다 더 잘 동작합니다.

긴 컨텍스트 모범 사례 (Best Practice for Long Context)

Chunked Prefill 크기 튜닝 (Tuning the Chunked Prefill Size)

chunked prefill 크기 최적화는 파이프라인 효율과 자원 활용을 균형 잡는 데 중요합니다. 이상적인 크기는 모델 아키텍처, 하드웨어 구성, 일반적인 입력 길이 등에 따라 달라집니다. 4K 같은 작은 청크 크기로 시작해서 특정 사용 사례에 최적의 크기를 찾을 때까지 점진적으로 늘리는 것을 권장합니다(대상 ITL과 PP 크기에 따라 최적 chunked prefill 크기가 다를 수 있으므로, 사용자는 스케일링에 사용 가능한 자원에 따라 베이스라인을 얻기 위해 반복해야 합니다). 또는 하드웨어 용량을 분석하고 roofline 모델에 기반해 최적 청크 크기를 결정할 수 있습니다.

초장 ITL을 위한 Dynamic Chunking 활성화와 스무딩 팩터 조정

SGLang은 성능을 더 개선할 수 있는 dynamic chunking 솔루션도 제공합니다. 이 기능은 현재 실험적 기능으로 일정량의 튜닝 실험이 필요하며 모든 워크로드에 적합하지 않을 수 있습니다. 또한 스무딩 팩터를 미세 조정하면 특정 워크로드와 모델 특성에 맞춰 성능을 최적화하는 데 도움이 됩니다.

NVIDIA H20 사례 연구 (Case Study on NVIDIA H20)

2K에서 16K까지 고정 chunked prefill 크기로 파이프라인 병렬화를 평가했을 때, 실험 결과 4K 청크 크기가 DeepSeek-V3.1에 최적의 prefill TTFT 성능을, 6K 청크 크기가 Qwen3-235B-A22B-FP8에 최적의 prefill TTFT 성능을 제공했습니다.

Dynamic chunking을 활성화할 때 먼저 최적 고정 chunked prefill 크기에 3을 곱해 초기 청크 크기로 확장했습니다. 실험을 통해 2~3의 승수가 적절한 균형을 제공한다는 것을 발견했습니다 — 과도한 초기 파이프라인 버블을 피하면서 컨텍스트 길이가 늘어남에 따라 후속 청크가 너무 작아지지 않게 합니다. 기본 dynamic chunking 스무딩 팩터 0.75로 파라미터 튜닝을 수행한 결과, DeepSeek-V3.1에는 12K 초기 청크 크기에서 0.65가, Qwen3-235B-A22B-FP8에는 18K 초기 청크 크기에서 0.8이 최적으로 동작한다는 것을 확인했습니다.

128K Input Token Length의 DeepSeek-V3.1

# prefill node 0 (fixed chunked prefill size)
python3 -m sglang.launch_server \
  --model-path deepseek-ai/DeepSeek-V3.1 --trust-remote-code \
  --nnodes 4 --node-rank 0 --tp 8 --pp-size 4 \
  --port 30000 --dist-init-addr <MASTER_NODE_IP> \
  --disable-radix-cache --mem-fraction-static 0.8  \
  --attention-backend fa3 --host 0.0.0.0 --watchdog-timeout 3600 \
  --max-running-requests 128 --chunked-prefill-size 4096
# prefill node 0 (with dynamic chunking)
export SGLANG_DYNAMIC_CHUNKING_SMOOTH_FACTOR=0.65
python3 -m sglang.launch_server \
  --model-path deepseek-ai/DeepSeek-V3.1 --trust-remote-code \
  --nnodes 4 --node-rank 0 --tp 8 --pp-size 4 \
  --port 30000 --dist-init-addr <MASTER_NODE_IP> \
  --disable-radix-cache --mem-fraction-static 0.8  \
  --attention-backend fa3 --host 0.0.0.0 --watchdog-timeout 3600 \
  --max-running-requests 128 --chunked-prefill-size 12288 --enable-dynamic-chunking

128K Input Token Length의 Qwen3-235B-A22B-FP8

# prefill node 0 (fixed chunked prefill size)
python3 -m sglang.launch_server \
  --model-path Qwen/Qwen3-235B-A22B-FP8 --trust-remote-code \
  --nnodes 4 --node-rank 0 --tp 4 --pp-size 8 \
  --port 30000 --dist-init-addr <MASTER_NODE_IP> \
  --disable-radix-cache --mem-fraction-static 0.8  \
  --attention-backend fa3 --host 0.0.0.0 --watchdog-timeout 3600 \
  --max-running-requests 128 --chunked-prefill-size 6144
# prefill node 0 (with dynamic chunking)
export SGLANG_DYNAMIC_CHUNKING_SMOOTH_FACTOR=0.8
python3 -m sglang.launch_server \
  --model-path Qwen/Qwen3-235B-A22B-FP8 --trust-remote-code \
  --nnodes 4 --node-rank 0 --tp 4 --pp-size 8 \
  --port 30000 --dist-init-addr <MASTER_NODE_IP> \
  --disable-radix-cache --mem-fraction-static 0.8  \
  --attention-backend fa3 --host 0.0.0.0 --watchdog-timeout 3600 \
  --max-running-requests 128 --chunked-prefill-size 18432 --enable-dynamic-chunking

참고: --disable-radix-cache는 재현 가능한 벤치마킹 목적으로만 활성화됩니다. 프로덕션에서 사용하는 것은 권장하지 않습니다.

PD Disaggregation과 파이프라인 병렬화 모범 사례 (Best Practice for Pipeline Parallelism with PD Disaggregation)

추가 예정입니다. PD Disaggregation과 함께하는 파이프라인 병렬화의 최신 업데이트를 지켜봐 주세요.

더 알아보기 (Learn more)