[Benchmark] Add sglang-embedding backend to bench_serving (#20017)
Co-authored-by: Satyam Kumar <satyamk@linkedin.com>
This commit is contained in:
co-authored by
Satyam Kumar
parent
61b228239e
commit
a54d71e967
+110
-35
@@ -47,6 +47,8 @@ from sglang.benchmark.utils import (
|
|||||||
|
|
||||||
_ROUTING_KEY_HEADER = "X-SMG-Routing-Key"
|
_ROUTING_KEY_HEADER = "X-SMG-Routing-Key"
|
||||||
|
|
||||||
|
_EMBEDDING_UNSUPPORTED_DATASETS = {"image", "mmmu", "mooncake"}
|
||||||
|
|
||||||
TERM_PLOTLIB_AVAILABLE = (importlib.util.find_spec("termplotlib") is not None) and (
|
TERM_PLOTLIB_AVAILABLE = (importlib.util.find_spec("termplotlib") is not None) and (
|
||||||
shutil.which("gnuplot") is not None
|
shutil.which("gnuplot") is not None
|
||||||
)
|
)
|
||||||
@@ -699,6 +701,56 @@ async def async_request_sglang_generate(
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
async def async_request_openai_embeddings(
|
||||||
|
request_func_input: RequestFuncInput,
|
||||||
|
pbar: Optional[tqdm] = None,
|
||||||
|
) -> RequestFuncOutput:
|
||||||
|
api_url = request_func_input.api_url
|
||||||
|
|
||||||
|
async with _create_bench_client_session() as session:
|
||||||
|
payload = {
|
||||||
|
"input": request_func_input.prompt,
|
||||||
|
"model": request_func_input.model,
|
||||||
|
}
|
||||||
|
|
||||||
|
if request_func_input.lora_name:
|
||||||
|
payload["model"] = request_func_input.lora_name
|
||||||
|
payload["lora_path"] = request_func_input.lora_name
|
||||||
|
|
||||||
|
payload.update(request_func_input.extra_request_body)
|
||||||
|
|
||||||
|
headers = get_request_headers()
|
||||||
|
if request_func_input.routing_key:
|
||||||
|
headers[_ROUTING_KEY_HEADER] = request_func_input.routing_key
|
||||||
|
|
||||||
|
output = RequestFuncOutput.init_new(request_func_input)
|
||||||
|
|
||||||
|
st = time.perf_counter()
|
||||||
|
output.start_time = st
|
||||||
|
try:
|
||||||
|
async with session.post(
|
||||||
|
url=api_url, json=payload, headers=headers
|
||||||
|
) as response:
|
||||||
|
if response.status == 200:
|
||||||
|
await response.json()
|
||||||
|
output.latency = time.perf_counter() - st
|
||||||
|
output.success = True
|
||||||
|
output.output_len = 0
|
||||||
|
else:
|
||||||
|
output.error = (
|
||||||
|
(response.reason or "") + ": " + (await response.text())
|
||||||
|
)
|
||||||
|
output.success = False
|
||||||
|
except Exception:
|
||||||
|
output.success = False
|
||||||
|
exc_info = sys.exc_info()
|
||||||
|
output.error = "".join(traceback.format_exception(*exc_info))
|
||||||
|
|
||||||
|
if pbar:
|
||||||
|
pbar.update(1)
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
async def async_request_gserver(
|
async def async_request_gserver(
|
||||||
request_func_input: RequestFuncInput,
|
request_func_input: RequestFuncInput,
|
||||||
pbar: Optional[tqdm] = None,
|
pbar: Optional[tqdm] = None,
|
||||||
@@ -808,6 +860,7 @@ ASYNC_REQUEST_FUNCS = {
|
|||||||
"sglang-native": async_request_sglang_generate,
|
"sglang-native": async_request_sglang_generate,
|
||||||
"sglang-oai": async_request_openai_completions,
|
"sglang-oai": async_request_openai_completions,
|
||||||
"sglang-oai-chat": async_request_openai_chat_completions,
|
"sglang-oai-chat": async_request_openai_chat_completions,
|
||||||
|
"sglang-embedding": async_request_openai_embeddings,
|
||||||
"vllm": async_request_openai_completions,
|
"vllm": async_request_openai_completions,
|
||||||
"vllm-chat": async_request_openai_chat_completions,
|
"vllm-chat": async_request_openai_chat_completions,
|
||||||
"lmdeploy": async_request_openai_completions,
|
"lmdeploy": async_request_openai_completions,
|
||||||
@@ -1403,12 +1456,15 @@ async def benchmark(
|
|||||||
"Total input vision tokens:", metrics.total_input_vision
|
"Total input vision tokens:", metrics.total_input_vision
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print("{:<40} {:<10}".format("Total generated tokens:", metrics.total_output))
|
is_embedding = backend == "sglang-embedding"
|
||||||
print(
|
if not is_embedding:
|
||||||
"{:<40} {:<10}".format(
|
print("{:<40} {:<10}".format("Total generated tokens:", metrics.total_output))
|
||||||
"Total generated tokens (retokenized):", metrics.total_output_retokenized
|
print(
|
||||||
|
"{:<40} {:<10}".format(
|
||||||
|
"Total generated tokens (retokenized):",
|
||||||
|
metrics.total_output_retokenized,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
print(
|
print(
|
||||||
"{:<40} {:<10.2f}".format(
|
"{:<40} {:<10.2f}".format(
|
||||||
"Request throughput (req/s):", metrics.request_throughput
|
"Request throughput (req/s):", metrics.request_throughput
|
||||||
@@ -1419,26 +1475,29 @@ async def benchmark(
|
|||||||
"Input token throughput (tok/s):", metrics.input_throughput
|
"Input token throughput (tok/s):", metrics.input_throughput
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print(
|
if not is_embedding:
|
||||||
"{:<40} {:<10.2f}".format(
|
print(
|
||||||
"Output token throughput (tok/s):", metrics.output_throughput
|
"{:<40} {:<10.2f}".format(
|
||||||
|
"Output token throughput (tok/s):", metrics.output_throughput
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
print(
|
||||||
print(
|
"{:<40} {:<10.2f}".format(
|
||||||
"{:<40} {:<10.2f}".format(
|
"Peak output token throughput (tok/s):",
|
||||||
"Peak output token throughput (tok/s):", metrics.max_output_tokens_per_s
|
metrics.max_output_tokens_per_s,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
print(
|
print(
|
||||||
"{:<40} {:<10}".format(
|
"{:<40} {:<10}".format(
|
||||||
"Peak concurrent requests:", metrics.max_concurrent_requests
|
"Peak concurrent requests:", metrics.max_concurrent_requests
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print(
|
if not is_embedding:
|
||||||
"{:<40} {:<10.2f}".format(
|
print(
|
||||||
"Total token throughput (tok/s):", metrics.total_throughput
|
"{:<40} {:<10.2f}".format(
|
||||||
|
"Total token throughput (tok/s):", metrics.total_throughput
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
print("{:<40} {:<10.2f}".format("Concurrency:", metrics.concurrency))
|
print("{:<40} {:<10.2f}".format("Concurrency:", metrics.concurrency))
|
||||||
if accept_length:
|
if accept_length:
|
||||||
print("{:<40} {:<10.2f}".format("Accept length:", accept_length))
|
print("{:<40} {:<10.2f}".format("Accept length:", accept_length))
|
||||||
@@ -1457,22 +1516,25 @@ async def benchmark(
|
|||||||
print(
|
print(
|
||||||
"{:<40} {:<10.2f}".format("P99 E2E Latency (ms):", metrics.p99_e2e_latency_ms)
|
"{:<40} {:<10.2f}".format("P99 E2E Latency (ms):", metrics.p99_e2e_latency_ms)
|
||||||
)
|
)
|
||||||
print("{s:{c}^{n}}".format(s="Time to First Token", n=50, c="-"))
|
if not is_embedding:
|
||||||
print("{:<40} {:<10.2f}".format("Mean TTFT (ms):", metrics.mean_ttft_ms))
|
print("{s:{c}^{n}}".format(s="Time to First Token", n=50, c="-"))
|
||||||
print("{:<40} {:<10.2f}".format("Median TTFT (ms):", metrics.median_ttft_ms))
|
print("{:<40} {:<10.2f}".format("Mean TTFT (ms):", metrics.mean_ttft_ms))
|
||||||
print("{:<40} {:<10.2f}".format("P99 TTFT (ms):", metrics.p99_ttft_ms))
|
print("{:<40} {:<10.2f}".format("Median TTFT (ms):", metrics.median_ttft_ms))
|
||||||
print(
|
print("{:<40} {:<10.2f}".format("P99 TTFT (ms):", metrics.p99_ttft_ms))
|
||||||
"{s:{c}^{n}}".format(s="Time per Output Token (excl. 1st token)", n=50, c="-")
|
print(
|
||||||
)
|
"{s:{c}^{n}}".format(
|
||||||
print("{:<40} {:<10.2f}".format("Mean TPOT (ms):", metrics.mean_tpot_ms))
|
s="Time per Output Token (excl. 1st token)", n=50, c="-"
|
||||||
print("{:<40} {:<10.2f}".format("Median TPOT (ms):", metrics.median_tpot_ms))
|
)
|
||||||
print("{:<40} {:<10.2f}".format("P99 TPOT (ms):", metrics.p99_tpot_ms))
|
)
|
||||||
print("{s:{c}^{n}}".format(s="Inter-Token Latency", n=50, c="-"))
|
print("{:<40} {:<10.2f}".format("Mean TPOT (ms):", metrics.mean_tpot_ms))
|
||||||
print("{:<40} {:<10.2f}".format("Mean ITL (ms):", metrics.mean_itl_ms))
|
print("{:<40} {:<10.2f}".format("Median TPOT (ms):", metrics.median_tpot_ms))
|
||||||
print("{:<40} {:<10.2f}".format("Median ITL (ms):", metrics.median_itl_ms))
|
print("{:<40} {:<10.2f}".format("P99 TPOT (ms):", metrics.p99_tpot_ms))
|
||||||
print("{:<40} {:<10.2f}".format("P95 ITL (ms):", metrics.p95_itl_ms))
|
print("{s:{c}^{n}}".format(s="Inter-Token Latency", n=50, c="-"))
|
||||||
print("{:<40} {:<10.2f}".format("P99 ITL (ms):", metrics.p99_itl_ms))
|
print("{:<40} {:<10.2f}".format("Mean ITL (ms):", metrics.mean_itl_ms))
|
||||||
print("{:<40} {:<10.2f}".format("Max ITL (ms):", metrics.max_itl_ms))
|
print("{:<40} {:<10.2f}".format("Median ITL (ms):", metrics.median_itl_ms))
|
||||||
|
print("{:<40} {:<10.2f}".format("P95 ITL (ms):", metrics.p95_itl_ms))
|
||||||
|
print("{:<40} {:<10.2f}".format("P99 ITL (ms):", metrics.p99_itl_ms))
|
||||||
|
print("{:<40} {:<10.2f}".format("Max ITL (ms):", metrics.max_itl_ms))
|
||||||
print("=" * 50)
|
print("=" * 50)
|
||||||
|
|
||||||
resp = requests.get(base_url + "/get_server_info", headers=get_auth_headers())
|
resp = requests.get(base_url + "/get_server_info", headers=get_auth_headers())
|
||||||
@@ -1670,7 +1732,13 @@ def run_benchmark(args_: argparse.Namespace):
|
|||||||
else f"http://{args.host}:{args.port}/v1/models"
|
else f"http://{args.host}:{args.port}/v1/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
if args.backend in ["sglang", "sglang-native"]:
|
if args.backend == "sglang-embedding":
|
||||||
|
api_url = (
|
||||||
|
f"{args.base_url}/v1/embeddings"
|
||||||
|
if args.base_url
|
||||||
|
else f"http://{args.host}:{args.port}/v1/embeddings"
|
||||||
|
)
|
||||||
|
elif args.backend in ["sglang", "sglang-native"]:
|
||||||
api_url = (
|
api_url = (
|
||||||
f"{args.base_url}/generate"
|
f"{args.base_url}/generate"
|
||||||
if args.base_url
|
if args.base_url
|
||||||
@@ -1739,12 +1807,19 @@ def run_benchmark(args_: argparse.Namespace):
|
|||||||
print("No model specified or found. Please provide a model using `--model`.")
|
print("No model specified or found. Please provide a model using `--model`.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if not check_chat_template(args.model):
|
if args.backend != "sglang-embedding" and not check_chat_template(args.model):
|
||||||
print(
|
print(
|
||||||
"\nWARNING It is recommended to use the `Chat` or `Instruct` model for benchmarking.\n"
|
"\nWARNING It is recommended to use the `Chat` or `Instruct` model for benchmarking.\n"
|
||||||
"Because when the tokenizer counts the output tokens, if there is gibberish, it might count incorrectly.\n"
|
"Because when the tokenizer counts the output tokens, if there is gibberish, it might count incorrectly.\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
args.backend == "sglang-embedding"
|
||||||
|
and args.dataset_name in _EMBEDDING_UNSUPPORTED_DATASETS
|
||||||
|
):
|
||||||
|
print(f"{args.dataset_name} dataset is unsupported for embeddings benchmark")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if args.dataset_name in ["image", "mmmu"]:
|
if args.dataset_name in ["image", "mmmu"]:
|
||||||
args.apply_chat_template = True
|
args.apply_chat_template = True
|
||||||
assert (
|
assert (
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ class BaseDataset(ABC):
|
|||||||
|
|
||||||
|
|
||||||
def compute_random_lens(full_len: int, range_ratio: float, num: int) -> List[int]:
|
def compute_random_lens(full_len: int, range_ratio: float, num: int) -> List[int]:
|
||||||
|
# full_len=0 is valid for embedding benchmarks where no output tokens are generated
|
||||||
|
if full_len <= 0:
|
||||||
|
return [0] * num
|
||||||
return np.random.randint(
|
return np.random.randint(
|
||||||
max(int(full_len * range_ratio), 1),
|
max(int(full_len * range_ratio), 1),
|
||||||
full_len + 1,
|
full_len + 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user