Bench Serving 가이드
Bench Serving 가이드
SGLang 서버가 실제로 어느 정도 처리량(throughput)과 지연(latency)을 내는지 알고 싶을 때 쓰는 도구가 python -m sglang.bench_serving이에요. 온라인 서빙 성능을 벤치마크하는 걸 중심으로, 데이터셋을 어떻게 고르고, 백엔드를 어떻게 바꾸고, 어떤 메트릭이 나오는지까지 강사 목소리로 정리해 드릴게요. 모든 명령어와 값은 원문 그대로 보존했어요.
이 가이드는 python -m sglang.bench_serving을 사용해 온라인 서빙 처리량과 지연을 벤치마크하는 방법을 설명해요. OpenAI 호환 및 네이티브 엔드포인트를 통해 여러 추론 백엔드를 지원하고, 콘솔 메트릭과 선택적인 JSONL 출력을 모두 생성해요.
이 도구가 하는 일
- 합성 또는 데이터셋 기반 프롬프트를 생성해 대상 서빙 엔드포인트에 제출해요.
- 처리량, 첫 토큰까지의 시간(TTFT), 토큰 간 지연(ITL), 요청별 종단 간 지연 등을 측정해요.
- 스트리밍/비스트리밍 모드, 속도 제어, 동시성 제한을 지원해요.
지원되는 백엔드와 엔드포인트
sglang/sglang-native:POST /generatesglang-oai,vllm,lmdeploy:POST /v1/completionssglang-oai-chat,vllm-chat,lmdeploy-chat:POST /v1/chat/completionssglang-embedding,vllm-embedding:POST /v1/embeddingstrt(TensorRT-LLM):POST /v2/models/ensemble/generate_streamgserver: 커스텀 서버 (이 스크립트에선 아직 구현되지 않음)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).
예시
- 요청당 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
- 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
- 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_lensttfts,itls(요청별: ITL 배열)generated_texts,errors
종단 간 예시
- 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
- 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
- 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
- 채팅 템플릿이 있는 이미지 (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
- 생성된 공유 프리픽스 (긴 시스템 프롬프트 + 짧은 질문):
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_distribution과 zipf_alpha를 포함하므로 uniform 모드와 zipf 모드 실행(또는 서로 다른 alpha를 가진 두 zipf 실행)이 캐시 파일을 절대 공유하지 않아요. Uniform 모드 파일 이름은 레거시 형식과 동일하게 유지되어 기존 캐시는 계속 유효해요.
이 플래그는 프리픽스 인기도 형태만 제어해요. 자체로는 어떤 프로덕션 트레이스를 재현하거나 특정 엔진의 관찰된 캐시 히트율을 보장하지 않아요.
- 엄격한 길이 제어를 위한 토크나이즈된 프롬프트 (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
- 프로파일링과 캐시 플러시 (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
- 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
- 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
- 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)
- 벤치마크와 프로파일링 — 네 벤치마크 도구와 프로파일링 전반
- 하이퍼파라미터 튜닝 — 처리량을 높이기 위한 파라미터 조정