Bench Serving 가이드

Bench Serving 가이드

SGLang 서버가 실제로 어느 정도 처리량(throughput)과 지연(latency)을 내는지 알고 싶을 때 쓰는 도구가 python -m sglang.bench_serving이에요. 온라인 서빙 성능을 벤치마크하는 걸 중심으로, 데이터셋을 어떻게 고르고, 백엔드를 어떻게 바꾸고, 어떤 메트릭이 나오는지까지 강사 목소리로 정리해 드릴게요. 모든 명령어와 값은 원문 그대로 보존했어요.

출처: 공식문서 - Bench Serving Guide

이 가이드는 python -m sglang.bench_serving을 사용해 온라인 서빙 처리량과 지연을 벤치마크하는 방법을 설명해요. OpenAI 호환 및 네이티브 엔드포인트를 통해 여러 추론 백엔드를 지원하고, 콘솔 메트릭과 선택적인 JSONL 출력을 모두 생성해요.

이 도구가 하는 일

  • 합성 또는 데이터셋 기반 프롬프트를 생성해 대상 서빙 엔드포인트에 제출해요.
  • 처리량, 첫 토큰까지의 시간(TTFT), 토큰 간 지연(ITL), 요청별 종단 간 지연 등을 측정해요.
  • 스트리밍/비스트리밍 모드, 속도 제어, 동시성 제한을 지원해요.

지원되는 백엔드와 엔드포인트

  • sglang / sglang-native: POST /generate
  • sglang-oai, vllm, lmdeploy: POST /v1/completions
  • sglang-oai-chat, vllm-chat, lmdeploy-chat: POST /v1/chat/completions
  • sglang-embedding, vllm-embedding: POST /v1/embeddings
  • trt (TensorRT-LLM): POST /v2/models/ensemble/generate_stream
  • gserver: 커스텀 서버 (이 스크립트에선 아직 구현되지 않음)
  • truss: POST /v1/models/model:predict

--base-url이 제공되면 그쪽으로 요청을 보내요. 그렇지 않으면 --host--port를 사용해요. --model이 제공되지 않으면 스크립트가 GET /v1/models에 질의해 사용 가능한 모델 ID를 찾으려 시도해요(OpenAI 호환 엔드포인트).

전제 조건

  • Python 3.10+
  • 이 스크립트가 일반적으로 쓰는 의존성: aiohttp, numpy, requests, tqdm, transformers, 그리고 일부 데이터셋은 datasets, pillow, pybase64. 필요에 따라 설치하세요.
  • 위 엔드포인트를 통해 도달 가능하게 실행 중인 추론 서버.
  • 서버에 인증이 필요하면 환경 변수 OPENAI_API_KEY를 설정하세요(`Authorization: Bearer ***로 사용).

빠른 시작

/generate를 노출하는 sglang 서버에 대한 기본 벤치마크를 실행해 봐요.

python3 -m sglang.launch_server --model-path meta-llama/Llama-3.1-8B-Instruct
python3 -m sglang.bench_serving \
  --backend sglang \
  --host 127.0.0.1 --port 30000 \
  --num-prompts 1000 \
  --model meta-llama/Llama-3.1-8B-Instruct

또는 OpenAI 호환 엔드포인트(completions)를 사용해요.

python3 -m sglang.bench_serving \
  --backend vllm \
  --base-url http://127.0.0.1:8000 \
  --num-prompts 1000 \
  --model meta-llama/Llama-3.1-8B-Instruct

공정한 임베딩 비교

두 임베딩 백엔드를 같은 모델, 토크나이저, 입력 길이, 프롬프트 수, 동시성으로 사용하세요. 벤치마크는 입력 토큰 처리량과 종단 간 지연을 보고해요. 임베딩에는 decode 쪽 TTFT나 TPOT가 없어요.

# Start either server on the same hardware and precision, then run one at a time.
python3 -m sglang.bench_serving \
  --backend sglang-embedding \
  --model google/embeddinggemma-300m \
  --dataset-name random \
  --random-input-len 2048 \
  --num-prompts 300 \
  --max-concurrency 64 \
  --warmup-requests 3 \
  --flush-cache
# vLLM's cache reset endpoint requires VLLM_SERVER_DEV_MODE=1 at server startup.
python3 -m sglang.bench_serving \
  --backend vllm-embedding \
  --model google/embeddinggemma-300m \
  --dataset-name random \
  --random-input-len 2048 \
  --num-prompts 300 \
  --max-concurrency 64 \
  --warmup-requests 3 \
  --flush-cache

--flush-cache는 워밍업 후 SGLang용 /flush_cache, vLLM용 /reset_prefix_cache를 호출해요. vLLM의 경우 서버를 VLLM_SERVER_DEV_MODE=1로 시작해요. 없으면 벤치마크는 실수로 warm-cache 성능을 측정하지 않도록 크게 실패해요.

데이터셋

--dataset-name으로 선택해요.

  • sharegpt (기본값): ShareGPT 스타일 쌍을 로드하고, 선택적으로 --sharegpt-context-len으로 제한하고 --sharegpt-output-len으로 출력을 재정의해요.
  • random: 랜덤 텍스트 길이. ShareGPT 토큰 공간에서 샘플링.
  • random-ids: 랜덤 토큰 id (횡설수설이 될 수 있음).
  • image: 이미지를 생성해 채팅 메시지에 감싸요. 커스텀 해상도, 여러 형식, 다른 콘텐츠 유형을 지원해요.
  • generated-shared-prefix: 긴 공유 시스템 프롬프트와 짧은 질문이 있는 합성 데이터셋.
  • mmmu: MMMU(Math split)에서 샘플링하고 이미지를 포함해요.
  • speed-bench: SPEED-Bench(SPEculative Evaluation Dataset) — Speculative Decoding (SD) 알고리즘을 평가하는 통합 벤치마크. 고정 길이 입력 시퀀스(1K–32K 토큰)를 세 가지 출력 엔트로피 범주(low_entropy, mixed, high_entropy)로 그룹화한 Throughput split을 사용해요. --dataset-path로 전달하는 미리 다운로드된 JSONL 파일이 필요해요.
  • agentic-trace: 미리 구축된 다중 턴 에이전트 트레이스(예: OpenHands / SWE-smith)를 재생해요. 각 대화는 턴 단위로 재생되며, 서버의 실제 어시스턴트 응답을 다음 턴의 기록에 다시 공급해요. 채팅 백엔드(--backend sglang-oai-chat)와 --dataset-path로 전달하는 트레이스 JSON이 필요해요.

일반 데이터셋 플래그:

  • --num-prompts N: 요청 수.
  • --random-input-len, --random-output-len, --random-range-ratio: random/random-ids/image용.
  • --image-count: 요청당 이미지 수 (image 데이터셋용).
  • --apply-chat-template: 프롬프트 구성 시 토크나이저 채팅 템플릿 적용.
  • --dataset-path PATH: ShareGPT json 파일 경로. 비어 있고 없으면 다운로드되어 캐시돼요.

생성된 공유 프리픽스 플래그 (generated-shared-prefix용):

  • --gsp-num-groups
  • --gsp-prompts-per-group
  • --gsp-system-prompt-len
  • --gsp-question-len
  • --gsp-output-len
  • --gsp-group-distribution {uniform,zipf}: 요청별 프리픽스-그룹 샘플링 분포(기본값: uniform). zipf에서는 각 요청의 그룹이 p(rank) = (1/rank**alpha) / sum_k(1/k**alpha)로 순위에 따라 샘플링되고, 순위는 1부터 시작하며 그룹 인덱스 0이 가장 뜨거워요. 온디스크 데이터셋 캐시는 (group_distribution, zipf_alpha)마다 별개의 키를 사용해 uniform 모드 캐시가 zipf 모드 캐시와 섞이지 않아요.
  • --gsp-zipf-alpha FLOAT: --gsp-group-distribution=zipf용 Zipf 지수. 0보다 엄격히 큰 유한한 float여야 해요. 값이 클수록 요청이 순위가 낮은(더 뜨거운) 그룹에 집중돼요. 분포가 zipf일 때 필수이고, 그 외에는 생략해야 해요.

이미지 데이터셋 플래그 (image용):

  • --image-count: 요청당 이미지 수.
  • --image-resolution: 이미지 해상도. 프리셋(4k, 1080p, 720p, 360p) 또는 커스텀 'heightxwidth' 형식(예: 1080x1920, 512x768)을 지원해요.
  • --image-format: 이미지 형식 (jpeg 또는 png).
  • --image-content: 이미지 콘텐츠 유형 (random 또는 blank).

Agentic trace 플래그 (agentic-trace용):

  • --dataset-path: 미리 구축된 트레이스 JSON 경로.
  • --sharegpt-output-len: 턴별 출력 길이 (기본값: 220).
  • --dataset-offset: 샘플링 전 대화 목록을 이 항목 수만큼 회전시켜 연속 스윕 단계가 새 대화에서 시작되게 해요.
  • --agentic-max-turns: 각 대화를 최대 이만큼의 턴으로 제한해요(작고 빠른 프로파일링 실행에 유용).

SPEED-Bench 플래그 (speed-bench용):

  • --dataset-path PATH: 미리 다운로드된 SPEED-Bench Throughput JSONL 경로(예: throughput_1k.jsonl). SPEED-Bench 측정 프레임워크로 생성하세요.
  • --speed-bench-category: 엔트로피 범주 하나로 필터링: low_entropy, mixed, high_entropy (기본값: 모두).
  • --speed-bench-output-len: 요청당 고정 출력 토큰 수 (기본값: 512).

예시

  1. 요청당 3개 이미지, 500개 프롬프트, 512 입력 길이, 512 출력 길이로 이미지 데이터셋을 벤치마크하려면,
python -m sglang.launch_server --model-path Qwen/Qwen2.5-VL-3B-Instruct --disable-radix-cache
python -m sglang.bench_serving \
    --backend sglang-oai-chat \
    --dataset-name image \
    --num-prompts 500 \
    --image-count 3 \
    --image-resolution 720p \
    --random-input-len 512 \
    --random-output-len 512
  1. 3000개 프롬프트, 1024 입력 길이, 1024 출력 길이로 random 데이터셋을 벤치마크하려면,
python -m sglang.launch_server --model-path Qwen/Qwen2.5-3B-Instruct
python3 -m sglang.bench_serving \
    --backend sglang \
    --dataset-name random \
    --num-prompts 3000 \
    --random-input 1024 \
    --random-output 1024 \
    --random-range-ratio 0.5
  1. SPEED-Bench(mixed 엔트로피 범주, 1K ISL)로 스펙큘레이티브 디코딩 처리량을 벤치마크하려면,
python -m sglang.launch_server --model-path meta-llama/Llama-3.1-8B-Instruct \
    --speculative-algorithm EAGLE --speculative-draft-model-path <draft-model-path>
python3 -m sglang.bench_serving \
    --backend sglang \
    --dataset-name speed-bench \
    --dataset-path /path/to/throughput_1k.jsonl \
    --speed-bench-category mixed \
    --speed-bench-output-len 512 \
    --num-prompts 512

모델과 토크나이저 선택하기

  • 백엔드가 GET /v1/models를 노출하지 않는 한 --model은 필수예요. 그 경우 첫 번째 모델 ID가 자동 선택돼요.
  • --tokenizer는 기본적으로 --model을 사용해요. 둘 다 HF 모델 ID 또는 로컬 경로가 될 수 있어요.
  • ModelScope 워크플로의 경우 SGLANG_USE_MODELSCOPE=true를 설정하면 ModelScope를 통해 가져올 수 있어요(속도를 위해 가중치는 건너뜀).
  • 토크나이저에 채팅 템플릿이 없으면 스크립트가 경고해요. 횡설수설 출력의 토큰 계산이 덜 견고할 수 있기 때문이에요.

속도, 동시성, 스트리밍

  • --request-rate: 초당 요청 수. inf는 모두 즉시 전송(버스트). 유한이 아닌 속도는 도착 시간에 Poisson 프로세스를 사용해요.
  • --max-concurrency: 도착 속도와 무관하게 동시에 처리 중인 대기 요청을 제한해요.
  • --disable-stream: 지원될 때 비스트리밍 모드로 전환. chat completions에서 TTFT는 그때 총 지연과 같아져요.

기타 주요 옵션

  • --output-file FILE.jsonl: JSONL 결과를 파일에 추가. 지정하지 않으면 자동 이름.
  • --output-details: 요청별 배열(생성된 텍스트, 오류, ttfts, itls, 입/출력 길이) 포함.
  • `--extra-request-body '{"top_p":0.9,"temperature":0.6}': 페이로드에 병합(샘플링 파라미터 등).
  • --disable-ignore-eos: EOS 동작을 그대로 통과(백엔드마다 다름).
  • --warmup-requests N: 먼저 짧은 출력으로 워밍업 요청 실행(기본값 1).
  • --flush-cache: 본 실행 전에 /flush_cache(sglang) 호출.
  • --profile: /start_profile/stop_profile 호출(서버가 예: SGLANG_TORCH_PROFILER_DIR로 프로파일링을 켜야 함).
  • --lora-name name1 name2 ...: 요청마다 하나를 무작위로 골라 백엔드에 전달(예: sglang용 lora_path).
  • --tokenize-prompt: 텍스트 대신 정수 ID 전송(현재 --backend sglang만 지원).

인증

대상 엔드포인트가 OpenAI 스타일 인증을 요구한다면,

export OPENAI_API_KEY=sk-...yourkey...

를 설정하세요. 스크립트는 OpenAI 호환 라우트에 `Authorization: Bearer *** 자동으로 추가해요.

메트릭 설명

각 실행 후 출력돼요.

  • 요청 처리량 (req/s)
  • 입력 토큰 처리량 (tok/s) - 텍스트와 비전 토큰 모두 포함
  • 출력 토큰 처리량 (tok/s)
  • 총 토큰 처리량 (tok/s) - 텍스트와 비전 토큰 모두 포함
  • 총 입력 텍스트 토큰 및 총 입력 비전 토큰 - modality별 세분화
  • 동시성: 모든 요청의 총 시간을 벽시계 시간으로 나눈 값
  • 종단 간 지연 (ms): 요청별 총 지연의 mean/median/std/p99
  • 첫 토큰까지의 시간 (TTFT, ms): 스트리밍 모드용 mean/median/std/p99
  • 토큰 간 지연 (ITL, ms): 토큰 사이의 mean/median/std/p95/p99/max
  • TPOT (ms): 첫 토큰 이후의 토큰 처리 시간, 즉 (latency - ttft)/(tokens-1)
  • Accept length (sglang 전용, 가능하면): 스펙큘레이티브 디코딩 수용 길이

스크립트는 생성된 텍스트를 구성된 토크나이저로 다시 토크나이즈하고 "retokenized" 수를 보고해요.

JSONL 출력 형식

--output-file이 설정되면 실행마다 JSON 객체 하나가 추가돼요. 기본 필드:

  • 인자 요약: backend, dataset, request_rate, max_concurrency 등.
  • 기간과 합계: completed, total_input_tokens, total_output_tokens, retokenized 합계.
  • 콘솔에 출력된 처리량과 지연 통계.
  • 가능하면 accept_length (sglang).

--output-details와 함께라면 확장 객체에 배열도 포함돼요.

  • input_lens, output_lens
  • ttfts, itls (요청별: ITL 배열)
  • generated_texts, errors

종단 간 예시

  1. sglang 네이티브 /generate (스트리밍):
python3 -m sglang.bench_serving \
  --backend sglang \
  --host 127.0.0.1 --port 30000 \
  --model meta-llama/Llama-3.1-8B-Instruct \
  --dataset-name random \
  --random-input-len 1024 --random-output-len 1024 --random-range-ratio 0.5 \
  --num-prompts 2000 \
  --request-rate 100 \
  --max-concurrency 512 \
  --output-file sglang_random.jsonl --output-details
  1. OpenAI 호환 Completions (예: vLLM):
python3 -m sglang.bench_serving \
  --backend vllm \
  --base-url http://127.0.0.1:8000 \
  --model meta-llama/Llama-3.1-8B-Instruct \
  --dataset-name sharegpt \
  --num-prompts 1000 \
  --sharegpt-output-len 256
  1. OpenAI 호환 Chat Completions (스트리밍):
python3 -m sglang.bench_serving \
  --backend vllm-chat \
  --base-url http://127.0.0.1:8000 \
  --model meta-llama/Llama-3.1-8B-Instruct \
  --dataset-name random \
  --num-prompts 500 \
  --apply-chat-template
  1. 채팅 템플릿이 있는 이미지 (VLM):
python3 -m sglang.bench_serving \
  --backend sglang \
  --host 127.0.0.1 --port 30000 \
  --model your-vlm-model \
  --dataset-name image \
  --image-count 2 \
  --image-resolution 720p \
  --random-input-len 128 --random-output-len 256 \
  --num-prompts 200 \
  --apply-chat-template

4a) 커스텀 해상도의 이미지:

python3 -m sglang.bench_serving \
  --backend sglang \
  --host 127.0.0.1 --port 30000 \
  --model your-vlm-model \
  --dataset-name image \
  --image-count 1 \
  --image-resolution 512x768 \
  --random-input-len 64 --random-output-len 128 \
  --num-prompts 100 \
  --apply-chat-template

4b) PNG 형식과 blank 콘텐츠의 1080p 이미지:

python3 -m sglang.bench_serving \
  --backend sglang \
  --host 127.0.0.1 --port 30000 \
  --model your-vlm-model \
  --dataset-name image \
  --image-count 1 \
  --image-resolution 1080p \
  --image-format png \
  --image-content blank \
  --random-input-len 64 --random-output-len 128 \
  --num-prompts 100 \
  --apply-chat-template
  1. 생성된 공유 프리픽스 (긴 시스템 프롬프트 + 짧은 질문):
python3 -m sglang.bench_serving \
  --backend sglang \
  --host 127.0.0.1 --port 30000 \
  --model meta-llama/Llama-3.1-8B-Instruct \
  --dataset-name generated-shared-prefix \
  --gsp-num-groups 64 --gsp-prompts-per-group 16 \
  --gsp-system-prompt-len 2048 --gsp-question-len 128 --gsp-output-len 256 \
  --num-prompts 1024

Zipfian / power-law 프리픽스 인기도 (--gsp-group-distribution=zipf로 opt-in):

python3 -m sglang.bench_serving \
  --backend sglang \
  --host 127.0.0.1 --port 30000 \
  --model meta-llama/Llama-3.1-8B-Instruct \
  --dataset-name generated-shared-prefix \
  --gsp-num-groups 64 --gsp-prompts-per-group 16 \
  --gsp-system-prompt-len 2048 --gsp-question-len 128 --gsp-output-len 256 \
  --gsp-group-distribution zipf --gsp-zipf-alpha 1.2 \
  --seed 42

zipf 모드는 각 요청의 프리픽스 그룹을 순위 기반 분포 p(rank) = (1/rank**alpha) / sum_k(1/k**alpha)(순위는 1부터 시작)로 샘플링하므로 그룹 인덱스 0이 가장 뜨거워요. 총 요청 수는 num_groups * prompts_per_group로 유지돼요 — uniform 모드와 동일하고, 요청별 그룹 할당만 바뀌어요. alpha는 0보다 엄격히 큰 유한한 float여야 해요. 값이 클수록 요청이 순위가 낮은(더 뜨거운) 그룹에 집중돼요.

~/.cache/sglang/benchmark/gen_shared_prefix_*.pkl의 온디스크 데이터셋 캐시는 키에 group_distributionzipf_alpha를 포함하므로 uniform 모드와 zipf 모드 실행(또는 서로 다른 alpha를 가진 두 zipf 실행)이 캐시 파일을 절대 공유하지 않아요. Uniform 모드 파일 이름은 레거시 형식과 동일하게 유지되어 기존 캐시는 계속 유효해요.

이 플래그는 프리픽스 인기도 형태만 제어해요. 자체로는 어떤 프로덕션 트레이스를 재현하거나 특정 엔진의 관찰된 캐시 히트율을 보장하지 않아요.

  1. 엄격한 길이 제어를 위한 토크나이즈된 프롬프트 (id) (sglang 전용):
python3 -m sglang.bench_serving \
  --backend sglang \
  --host 127.0.0.1 --port 30000 \
  --model meta-llama/Llama-3.1-8B-Instruct \
  --dataset-name random \
  --tokenize-prompt \
  --random-input-len 2048 --random-output-len 256 --random-range-ratio 0.2
  1. 프로파일링과 캐시 플러시 (sglang):
python3 -m sglang.bench_serving \
  --backend sglang \
  --host 127.0.0.1 --port 30000 \
  --model meta-llama/Llama-3.1-8B-Instruct \
  --profile \
  --flush-cache
  1. TensorRT-LLM 스트리밍 엔드포인트:
python3 -m sglang.bench_serving \
  --backend trt \
  --base-url http://127.0.0.1:8000 \
  --model your-trt-llm-model \
  --dataset-name random \
  --num-prompts 100 \
  --disable-ignore-eos
  1. mooncake 트레이스로 대규모 KVCache 공유 평가 (sglang 전용):
python3 -m sglang.bench_serving \
  --backend sglang \
  --host 127.0.0.1 --port 30000 \
  --model model-name \
  --dataset-name mooncake \
  --mooncake-slowdown-factor 1.0 \
  --mooncake-num-rounds 1000 \
  --mooncake-workload conversation|mooncake|agent|synthetic \
  --use-trace-timestamps true \
  --random-output-len 256
  1. Fake decode 스트레스 테스트 (PD 분리, decode 전용):

PD 분리 설정에서 순수 decode 성능을 벤치마크할 때 --fake-prefill로 prefill 노드를 완전히 우회할 수 있어요. 이때 decode 서버를 --disaggregation-transfer-backend fake로 시작해야 해요.

# Step 1: Start a decode-only server with fake transfer backend
python -m sglang.launch_server \
  --model-path meta-llama/Llama-3.1-8B-Instruct \
  --disaggregation-mode decode \
  --disaggregation-transfer-backend fake \
  --port 30001

# Step 2: Run bench_serving with --fake-prefill
python3 -m sglang.bench_serving \
  --backend sglang \
  --host 127.0.0.1 --port 30001 \
  --model meta-llama/Llama-3.1-8B-Instruct \
  --dataset-name random \
  --num-prompts 500 \
  --random-input-len 1024 --random-output-len 256 \
  --fake-prefill

마찬가지로 bench_one_batch_server--fake-prefill을 지원해요.

python3 -m sglang.bench_one_batch_server \
  --base-url http://127.0.0.1:30001 \
  --model-path meta-llama/Llama-3.1-8B-Instruct \
  --batch-size 32 --input-len 1024 --output-len 256 \
  --fake-prefill

--fake-prefill 플래그는 각 요청에 특수 센티널 값을 자동으로 주입해서 decode 서버가 실제 KV 전송을 건너뛰고 로컬에서 fake KV 데이터를 생성하도록 지시해요.

문제 해결 (Troubleshooting)

  • 모든 요청이 실패: --backend, 서버 URL/포트, --model, 인증을 확인하세요. 스크립트가 출력한 워밍업 오류를 확인하세요.
  • 처리량이 너무 낮게 보임: --request-rate--max-concurrency를 조정하고, 서버 배치 크기/스케줄링을 확인하며, 적절하면 스트리밍이 켜져 있는지 확인하세요.
  • 토큰 수가 이상해 보임: 적절한 채팅 템플릿이 있는 chat/instruct 모델을 선호하세요. 아니면 횡설수설의 토큰화가 일관되지 않을 수 있어요.
  • 이미지/MMMU 데이터셋: 추가 의존성(pillow, datasets, pybase64)을 설치했는지 확인하세요.
  • 인증 오류 (401/403): OPENAI_API_KEY를 설정하거나 서버 인증을 비활성화하세요.

참고

  • 스크립트는 많은 동시 연결을 돕기 위해 파일 디스크립터 소프트 한계(RLIMIT_NOFILE)를 올려요.
  • sglang의 경우 실행 후 /server_info를 질의해 사용 가능할 때 스펙큘레이티브 디코딩 accept length를 보고해요.

더 알아보기 (Learn more)