Add peak output tokens per second in bench_serving (#14165)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
github-actions[bot]
parent
630a693081
commit
9c80072845
+3
-1
@@ -230,6 +230,7 @@ RUN --mount=type=cache,target=/var/cache/apt apt-get update && apt-get install -
|
|||||||
bear \
|
bear \
|
||||||
ccache \
|
ccache \
|
||||||
less \
|
less \
|
||||||
|
gnuplot \
|
||||||
&& apt install -y rdma-core infiniband-diags openssh-server perftest ibverbs-providers libibumad3 libibverbs1 libnl-3-200 libnl-route-3-200 librdmacm1 \
|
&& apt install -y rdma-core infiniband-diags openssh-server perftest ibverbs-providers libibumad3 libibverbs1 libnl-3-200 libnl-route-3-200 librdmacm1 \
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& apt-get clean
|
&& apt-get clean
|
||||||
@@ -258,7 +259,8 @@ RUN --mount=type=cache,target=/root/.cache/pip python3 -m pip install --break-sy
|
|||||||
pre-commit \
|
pre-commit \
|
||||||
pandas \
|
pandas \
|
||||||
matplotlib \
|
matplotlib \
|
||||||
tabulate
|
tabulate \
|
||||||
|
termplotlib
|
||||||
|
|
||||||
# Install diff-so-fancy
|
# Install diff-so-fancy
|
||||||
RUN curl -LSso /usr/local/bin/diff-so-fancy https://${GITHUB_ARTIFACTORY}/so-fancy/diff-so-fancy/releases/download/v1.4.4/diff-so-fancy \
|
RUN curl -LSso /usr/local/bin/diff-so-fancy https://${GITHUB_ARTIFACTORY}/so-fancy/diff-so-fancy/releases/download/v1.4.4/diff-so-fancy \
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ python3 -m sglang.bench_serving --backend sglang --dataset-name random --num-pro
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import importlib.util
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
import random
|
import random
|
||||||
import resource
|
import resource
|
||||||
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
@@ -47,6 +49,10 @@ from transformers import (
|
|||||||
|
|
||||||
ASSISTANT_SUFFIX = "Assistant:"
|
ASSISTANT_SUFFIX = "Assistant:"
|
||||||
|
|
||||||
|
TERM_PLOTLIB_AVAILABLE = (importlib.util.find_spec("termplotlib") is not None) and (
|
||||||
|
shutil.which("gnuplot") is not None
|
||||||
|
)
|
||||||
|
|
||||||
global args
|
global args
|
||||||
|
|
||||||
|
|
||||||
@@ -93,6 +99,7 @@ class RequestFuncOutput:
|
|||||||
prompt_len: int = 0
|
prompt_len: int = 0
|
||||||
error: str = ""
|
error: str = ""
|
||||||
output_len: int = 0
|
output_len: int = 0
|
||||||
|
start_time: float = 0.0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def init_new(request_func_input: RequestFuncInput):
|
def init_new(request_func_input: RequestFuncInput):
|
||||||
@@ -230,6 +237,7 @@ async def async_request_openai_completions(
|
|||||||
output_len = request_func_input.output_len
|
output_len = request_func_input.output_len
|
||||||
ttft = 0.0
|
ttft = 0.0
|
||||||
st = time.perf_counter()
|
st = time.perf_counter()
|
||||||
|
output.start_time = st
|
||||||
most_recent_timestamp = st
|
most_recent_timestamp = st
|
||||||
try:
|
try:
|
||||||
async with session.post(
|
async with session.post(
|
||||||
@@ -354,6 +362,7 @@ async def async_request_openai_chat_completions(
|
|||||||
output_len = request_func_input.output_len
|
output_len = request_func_input.output_len
|
||||||
ttft = 0.0
|
ttft = 0.0
|
||||||
st = time.perf_counter()
|
st = time.perf_counter()
|
||||||
|
output.start_time = st
|
||||||
most_recent_timestamp = st
|
most_recent_timestamp = st
|
||||||
try:
|
try:
|
||||||
async with session.post(
|
async with session.post(
|
||||||
@@ -543,6 +552,7 @@ async def async_request_sglang_generate(
|
|||||||
output_len = request_func_input.output_len
|
output_len = request_func_input.output_len
|
||||||
ttft = 0.0
|
ttft = 0.0
|
||||||
st = time.perf_counter()
|
st = time.perf_counter()
|
||||||
|
output.start_time = st
|
||||||
most_recent_timestamp = st
|
most_recent_timestamp = st
|
||||||
last_output_len = 0
|
last_output_len = 0
|
||||||
try:
|
try:
|
||||||
@@ -869,6 +879,8 @@ class BenchmarkMetrics:
|
|||||||
std_e2e_latency_ms: float
|
std_e2e_latency_ms: float
|
||||||
p99_e2e_latency_ms: float
|
p99_e2e_latency_ms: float
|
||||||
concurrency: float
|
concurrency: float
|
||||||
|
max_output_tokens_per_s: float = 0.0
|
||||||
|
max_concurrent_requests: int = 0
|
||||||
|
|
||||||
|
|
||||||
SHAREGPT_URL = "https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json"
|
SHAREGPT_URL = "https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered/resolve/main/ShareGPT_V3_unfiltered_cleaned_split.json"
|
||||||
@@ -1666,6 +1678,7 @@ def calculate_metrics(
|
|||||||
tokenizer: PreTrainedTokenizerBase,
|
tokenizer: PreTrainedTokenizerBase,
|
||||||
backend: str,
|
backend: str,
|
||||||
accept_length: Optional[float] = None,
|
accept_length: Optional[float] = None,
|
||||||
|
plot_throughput: bool = False,
|
||||||
) -> Tuple[BenchmarkMetrics, List[int]]:
|
) -> Tuple[BenchmarkMetrics, List[int]]:
|
||||||
output_lens: List[int] = []
|
output_lens: List[int] = []
|
||||||
retokenized_output_lens: List[int] = []
|
retokenized_output_lens: List[int] = []
|
||||||
@@ -1725,6 +1738,70 @@ def calculate_metrics(
|
|||||||
stacklevel=2,
|
stacklevel=2,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
max_output_tokens_per_s = 0.0
|
||||||
|
max_concurrent_requests = 0
|
||||||
|
|
||||||
|
successful_outputs = [output for output in outputs if output.success]
|
||||||
|
if successful_outputs:
|
||||||
|
min_start_time = min(output.start_time for output in successful_outputs)
|
||||||
|
max_end_time = max(
|
||||||
|
output.start_time + output.latency for output in successful_outputs
|
||||||
|
)
|
||||||
|
|
||||||
|
duration_seconds = int(np.ceil(max_end_time - min_start_time)) + 1
|
||||||
|
tokens_per_second = np.zeros(duration_seconds)
|
||||||
|
concurrent_requests_per_second = np.zeros(duration_seconds)
|
||||||
|
|
||||||
|
for output in outputs:
|
||||||
|
if not output.success:
|
||||||
|
continue
|
||||||
|
|
||||||
|
token_times = [output.start_time + output.ttft]
|
||||||
|
current_time = token_times[0]
|
||||||
|
for itl_value in output.itl:
|
||||||
|
current_time += itl_value
|
||||||
|
token_times.append(current_time)
|
||||||
|
|
||||||
|
for token_time in token_times:
|
||||||
|
second_bucket = int(token_time - min_start_time)
|
||||||
|
if 0 <= second_bucket < duration_seconds:
|
||||||
|
tokens_per_second[second_bucket] += 1
|
||||||
|
|
||||||
|
request_start_second = int(output.start_time - min_start_time)
|
||||||
|
request_end_second = int(
|
||||||
|
(output.start_time + output.latency) - min_start_time
|
||||||
|
)
|
||||||
|
|
||||||
|
for second in range(
|
||||||
|
request_start_second, min(request_end_second + 1, duration_seconds)
|
||||||
|
):
|
||||||
|
concurrent_requests_per_second[second] += 1
|
||||||
|
|
||||||
|
if len(tokens_per_second) > 0:
|
||||||
|
max_output_tokens_per_s = float(np.max(tokens_per_second))
|
||||||
|
max_concurrent_requests = int(np.max(concurrent_requests_per_second))
|
||||||
|
|
||||||
|
if plot_throughput:
|
||||||
|
if TERM_PLOTLIB_AVAILABLE:
|
||||||
|
import termplotlib as tpl
|
||||||
|
|
||||||
|
fig = tpl.figure()
|
||||||
|
fig.plot(
|
||||||
|
np.arange(len(tokens_per_second)),
|
||||||
|
tokens_per_second,
|
||||||
|
title="Output tokens per second",
|
||||||
|
xlabel="Time (s)",
|
||||||
|
)
|
||||||
|
fig.plot(
|
||||||
|
np.arange(len(concurrent_requests_per_second)),
|
||||||
|
concurrent_requests_per_second,
|
||||||
|
title="Concurrent requests per second",
|
||||||
|
xlabel="Time (s)",
|
||||||
|
)
|
||||||
|
fig.show()
|
||||||
|
else:
|
||||||
|
print("tip: install termplotlib and gnuplot to plot the metrics")
|
||||||
|
|
||||||
itls = retokenized_itls if use_retokenized_itl else itls
|
itls = retokenized_itls if use_retokenized_itl else itls
|
||||||
metrics = BenchmarkMetrics(
|
metrics = BenchmarkMetrics(
|
||||||
completed=completed,
|
completed=completed,
|
||||||
@@ -1760,6 +1837,8 @@ def calculate_metrics(
|
|||||||
std_e2e_latency_ms=np.std(e2e_latencies) * 1000,
|
std_e2e_latency_ms=np.std(e2e_latencies) * 1000,
|
||||||
p99_e2e_latency_ms=np.percentile(e2e_latencies, 99) * 1000,
|
p99_e2e_latency_ms=np.percentile(e2e_latencies, 99) * 1000,
|
||||||
concurrency=np.sum(e2e_latencies) / dur_s,
|
concurrency=np.sum(e2e_latencies) / dur_s,
|
||||||
|
max_output_tokens_per_s=max_output_tokens_per_s,
|
||||||
|
max_concurrent_requests=max_concurrent_requests,
|
||||||
)
|
)
|
||||||
|
|
||||||
return metrics, output_lens
|
return metrics, output_lens
|
||||||
@@ -2012,6 +2091,7 @@ async def benchmark(
|
|||||||
tokenizer=tokenizer,
|
tokenizer=tokenizer,
|
||||||
backend=backend,
|
backend=backend,
|
||||||
accept_length=accept_length,
|
accept_length=accept_length,
|
||||||
|
plot_throughput=args.plot_throughput,
|
||||||
)
|
)
|
||||||
|
|
||||||
print("\n{s:{c}^{n}}".format(s=" Serving Benchmark Result ", n=50, c="="))
|
print("\n{s:{c}^{n}}".format(s=" Serving Benchmark Result ", n=50, c="="))
|
||||||
@@ -2055,6 +2135,16 @@ async def benchmark(
|
|||||||
"Output token throughput (tok/s):", metrics.output_throughput
|
"Output token throughput (tok/s):", metrics.output_throughput
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
print(
|
||||||
|
"{:<40} {:<10.2f}".format(
|
||||||
|
"Peak output token throughput (tok/s):", metrics.max_output_tokens_per_s
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"{:<40} {:<10}".format(
|
||||||
|
"Peak concurrent requests:", metrics.max_concurrent_requests
|
||||||
|
)
|
||||||
|
)
|
||||||
print(
|
print(
|
||||||
"{:<40} {:<10.2f}".format(
|
"{:<40} {:<10.2f}".format(
|
||||||
"Total token throughput (tok/s):", metrics.total_throughput
|
"Total token throughput (tok/s):", metrics.total_throughput
|
||||||
@@ -2142,6 +2232,8 @@ async def benchmark(
|
|||||||
"p99_itl_ms": metrics.p99_itl_ms,
|
"p99_itl_ms": metrics.p99_itl_ms,
|
||||||
"concurrency": metrics.concurrency,
|
"concurrency": metrics.concurrency,
|
||||||
"accept_length": accept_length,
|
"accept_length": accept_length,
|
||||||
|
"max_output_tokens_per_s": metrics.max_output_tokens_per_s,
|
||||||
|
"max_concurrent_requests": metrics.max_concurrent_requests,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
print(f"Error running benchmark for request rate: {request_rate}")
|
print(f"Error running benchmark for request rate: {request_rate}")
|
||||||
@@ -2218,6 +2310,9 @@ def run_benchmark(args_: argparse.Namespace):
|
|||||||
if not hasattr(args, "tokenize_prompt"):
|
if not hasattr(args, "tokenize_prompt"):
|
||||||
args.tokenize_prompt = False
|
args.tokenize_prompt = False
|
||||||
|
|
||||||
|
if not hasattr(args, "plot_throughput"):
|
||||||
|
args.plot_throughput = False
|
||||||
|
|
||||||
if not hasattr(args, "use_trace_timestamps"):
|
if not hasattr(args, "use_trace_timestamps"):
|
||||||
args.use_trace_timestamps = False
|
args.use_trace_timestamps = False
|
||||||
if not hasattr(args, "mooncake_slowdown_factor"):
|
if not hasattr(args, "mooncake_slowdown_factor"):
|
||||||
@@ -2609,6 +2704,11 @@ if __name__ == "__main__":
|
|||||||
help="Use Torch Profiler. The endpoint must be launched with "
|
help="Use Torch Profiler. The endpoint must be launched with "
|
||||||
"SGLANG_TORCH_PROFILER_DIR to enable profiler.",
|
"SGLANG_TORCH_PROFILER_DIR to enable profiler.",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--plot-throughput",
|
||||||
|
action="store_true",
|
||||||
|
help="Plot throughput and concurrent requests over time. Requires termplotlib and gnuplot.",
|
||||||
|
)
|
||||||
# TODO unify all these
|
# TODO unify all these
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--profile-activities",
|
"--profile-activities",
|
||||||
|
|||||||
Reference in New Issue
Block a user