A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 14, 05:30 UTC
[Misc] Add request_id into benchmark_serve.py (#23065)
Signed-off-by: yangxia <yangxiast@gmail.com>
hustxiayang committed
Aug 19, 2025 at 04:32 UTC
31436e8b4f0a210cf5c6f9ec9b47fc1b5d7cc47a
6 files changed
+243
-46
benchmarks/backend_request_func.py
+21
-2
@@ -34,6 +34,7 @@ class RequestFuncInput:
34
multi_modal_content: Optional[dict | list[dict]] = None
35
ignore_eos: bool = False
36
language: Optional[str] = None
37
+ request_id: Optional[str] = None
38
39
40
@dataclass
@@ -71,6 +72,9 @@ async def async_request_tgi(
72
"inputs": request_func_input.prompt,
73
"parameters": params,
74
}
75
+ headers = None
76
+ if request_func_input.request_id:
77
+ headers = {"x-request-id": request_func_input.request_id}
78
output = RequestFuncOutput()
79
output.prompt_len = request_func_input.prompt_len
80
if request_func_input.ignore_eos:
@@ -82,7 +86,9 @@ async def async_request_tgi(
86
st = time.perf_counter()
87
most_recent_timestamp = st
88
try:
85
- async with session.post(url=api_url, json=payload) as response:
89
+ async with session.post(
90
+ url=api_url, json=payload, headers=headers
91
+ ) as response:
92
if response.status == 200:
93
async for chunk_bytes in response.content:
94
chunk_bytes = chunk_bytes.strip()
@@ -145,6 +151,9 @@ async def async_request_trt_llm(
151
}
152
if request_func_input.ignore_eos:
153
payload["min_length"] = request_func_input.output_len
154
+ headers = None
155
+ if request_func_input.request_id:
156
+ headers = {"x-request-id": request_func_input.request_id}
157
output = RequestFuncOutput()
158
output.prompt_len = request_func_input.prompt_len
159
@@ -152,7 +161,9 @@ async def async_request_trt_llm(
161
st = time.perf_counter()
162
most_recent_timestamp = st
163
try:
155
- async with session.post(url=api_url, json=payload) as response:
164
+ async with session.post(
165
+ url=api_url, json=payload, headers=headers
166
+ ) as response:
167
if response.status == 200:
168
async for chunk_bytes in response.content:
169
chunk_bytes = chunk_bytes.strip()
@@ -211,6 +222,8 @@ async def async_request_deepspeed_mii(
222
"top_p": 1.0,
223
}
224
headers = {"Authorization": f"Bearer {os.environ.get('OPENAI_API_KEY')}"}
225
+ if request_func_input.request_id:
226
+ headers["x-request-id"] = request_func_input.request_id
227
228
output = RequestFuncOutput()
229
output.prompt_len = request_func_input.prompt_len
@@ -283,6 +296,8 @@ async def async_request_openai_completions(
296
if request_func_input.extra_body:
297
payload.update(request_func_input.extra_body)
298
headers = {"Authorization": f"Bearer {os.environ.get('OPENAI_API_KEY')}"}
299
+ if request_func_input.request_id:
300
+ headers["x-request-id"] = request_func_input.request_id
301
302
output = RequestFuncOutput()
303
output.prompt_len = request_func_input.prompt_len
@@ -395,6 +410,8 @@ async def async_request_openai_chat_completions(
410
"Content-Type": "application/json",
411
"Authorization": f"Bearer {os.environ.get('OPENAI_API_KEY')}",
412
}
413
+ if request_func_input.request_id:
414
+ headers["x-request-id"] = request_func_input.request_id
415
416
output = RequestFuncOutput()
417
output.prompt_len = request_func_input.prompt_len
@@ -491,6 +508,8 @@ async def async_request_openai_audio(
508
headers = {
509
"Authorization": f"Bearer {os.environ.get('OPENAI_API_KEY')}",
510
}
511
+ if request_func_input.request_id:
512
+ headers["x-request-id"] = request_func_input.request_id
513
514
# Send audio file
515
def to_bytes(y, sr):
benchmarks/benchmark_dataset.py
+90
-19
@@ -19,6 +19,7 @@ import logging
19
import random
20
from abc import ABC, abstractmethod
21
from collections.abc import Mapping
22
+from copy import deepcopy
23
from dataclasses import dataclass
24
from functools import cache
25
from io import BytesIO
@@ -54,6 +55,7 @@ class SampleRequest:
55
expected_output_len: int
56
multi_modal_data: Optional[Union[MultiModalDataDict, dict, list[dict]]] = None
57
lora_request: Optional[LoRARequest] = None
58
+ request_id: Optional[str] = None
59
60
61
# -----------------------------------------------------------------------------
@@ -155,7 +157,10 @@ class BenchmarkDataset(ABC):
157
158
@abstractmethod
159
def sample(
158
- self, tokenizer: PreTrainedTokenizerBase, num_requests: int
160
+ self,
161
+ tokenizer: PreTrainedTokenizerBase,
162
+ num_requests: int,
163
+ request_id_prefix: str = "",
164
) -> list[SampleRequest]:
165
"""
166
Abstract method to generate sample requests from the dataset.
@@ -167,6 +172,7 @@ class BenchmarkDataset(ABC):
172
tokenizer (PreTrainedTokenizerBase): The tokenizer to be used
173
for processing the dataset's text.
174
num_requests (int): The number of sample requests to generate.
175
+ request_id_prefix (str) The prefix of request_id.
176
177
Returns:
178
list[SampleRequest]: A list of sample requests generated from the
@@ -175,7 +181,10 @@ class BenchmarkDataset(ABC):
181
raise NotImplementedError("sample must be implemented in subclasses.")
182
183
def maybe_oversample_requests(
178
- self, requests: list[SampleRequest], num_requests: int
184
+ self,
185
+ requests: list[SampleRequest],
186
+ num_requests: int,
187
+ request_id_prefix: str = "",
188
) -> None:
189
"""
190
Oversamples the list of requests if its size is less than the desired
@@ -183,11 +192,18 @@ class BenchmarkDataset(ABC):
192
193
Args:
194
requests (List[SampleRequest]): The current list of sampled
186
- requests. num_requests (int): The target number of requests.
195
+ requests.
196
+ num_requests (int): The target number of requests.
197
+ request_id_prefix (str) The prefix of the request ids.
198
"""
199
if len(requests) < num_requests:
200
random.seed(self.random_seed)
190
- additional = random.choices(requests, k=num_requests - len(requests))
201
+ additional = deepcopy(
202
+ random.choices(requests, k=num_requests - len(requests))
203
+ )
204
+ for i in range(len(additional)):
205
+ req = additional[i]
206
+ req.request_id = request_id_prefix + str(len(requests) + i)
207
requests.extend(additional)
208
logger.info("Oversampled requests to reach %d total samples.", num_requests)
209
@@ -303,6 +319,7 @@ class RandomDataset(BenchmarkDataset):
319
range_ratio: float = DEFAULT_RANGE_RATIO,
320
input_len: int = DEFAULT_INPUT_LEN,
321
output_len: int = DEFAULT_OUTPUT_LEN,
322
+ request_id_prefix: str = "",
323
**kwargs,
324
) -> list[SampleRequest]:
325
# Enforce range_ratio < 1
@@ -363,8 +380,10 @@ class RandomDataset(BenchmarkDataset):
380
prompt=prompt,
381
prompt_len=total_input_len,
382
expected_output_len=int(output_lens[i]),
383
+ request_id=request_id_prefix + str(i),
384
)
385
)
386
+
387
return requests
388
389
@@ -406,9 +425,11 @@ class ShareGPTDataset(BenchmarkDataset):
425
max_loras: Optional[int] = None,
426
output_len: Optional[int] = None,
427
enable_multimodal_chat: bool = False,
428
+ request_id_prefix: str = "",
429
**kwargs,
430
) -> list:
431
samples: list = []
432
+ ind = 0
433
for entry in self.data:
434
if len(samples) >= num_requests:
435
break
@@ -444,9 +465,11 @@ class ShareGPTDataset(BenchmarkDataset):
465
expected_output_len=new_output_len,
466
lora_request=lora_request,
467
multi_modal_data=mm_content,
468
+ request_id=request_id_prefix + str(ind),
469
)
470
)
449
- self.maybe_oversample_requests(samples, num_requests)
471
+ ind += 1
472
+ self.maybe_oversample_requests(samples, num_requests, request_id_prefix)
473
return samples
474
475
@@ -512,10 +535,11 @@ class CustomDataset(BenchmarkDataset):
535
output_len: Optional[int] = None,
536
enable_multimodal_chat: bool = False,
537
skip_chat_template: bool = False,
538
+ request_id_prefix: str = "",
539
**kwargs,
540
) -> list:
541
sampled_requests = []
518
- for item in self.data:
542
+ for i, item in enumerate(self.data):
543
if len(sampled_requests) >= num_requests:
544
break
545
prompt = item["prompt"]
@@ -534,9 +558,12 @@ class CustomDataset(BenchmarkDataset):
558
prompt=prompt,
559
prompt_len=prompt_len,
560
expected_output_len=output_len,
561
+ request_id=request_id_prefix + str(i),
562
)
563
)
539
- self.maybe_oversample_requests(sampled_requests, num_requests)
564
+ self.maybe_oversample_requests(
565
+ sampled_requests, num_requests, request_id_prefix
566
+ )
567
568
return sampled_requests
569
@@ -578,6 +605,7 @@ class SonnetDataset(BenchmarkDataset):
605
input_len: int = DEFAULT_INPUT_LEN,
606
output_len: int = DEFAULT_OUTPUT_LEN,
607
return_prompt_formatted: bool = False,
608
+ request_id_prefix: str = "",
609
**kwargs,
610
) -> list:
611
# Calculate average token length for a poem line.
@@ -603,6 +631,7 @@ class SonnetDataset(BenchmarkDataset):
631
prefix_lines = self.data[:num_prefix_lines]
632
633
samples = []
634
+ ind = 0
635
while len(samples) < num_requests:
636
extra_lines = random.choices(
637
self.data, k=num_input_lines - num_prefix_lines
@@ -613,14 +642,17 @@ class SonnetDataset(BenchmarkDataset):
642
msg, add_generation_prompt=True, tokenize=False
643
)
644
prompt_len = len(tokenizer(prompt_formatted).input_ids)
645
+
646
if prompt_len <= input_len:
647
samples.append(
648
SampleRequest(
649
prompt=prompt_formatted if return_prompt_formatted else prompt,
650
prompt_len=prompt_len,
651
expected_output_len=output_len,
652
+ request_id=request_id_prefix + str(ind),
653
)
654
)
655
+ ind += 1
656
return samples
657
658
@@ -672,6 +704,7 @@ class BurstGPTDataset(BenchmarkDataset):
704
num_requests: int,
705
max_loras: Optional[int] = None,
706
lora_path: Optional[str] = None,
707
+ request_id_prefix: str = "",
708
**kwargs,
709
) -> list[SampleRequest]:
710
samples = []
@@ -693,6 +726,7 @@ class BurstGPTDataset(BenchmarkDataset):
726
prompt_len=input_len,
727
expected_output_len=output_len,
728
lora_request=lora_req,
729
+ request_id=request_id_prefix + str(i),
730
)
731
)
732
return samples
@@ -752,12 +786,14 @@ class ConversationDataset(HuggingFaceDataset):
786
num_requests: int,
787
output_len: Optional[int] = None,
788
enable_multimodal_chat: bool = False,
789
+ request_id_prefix: str = "",
790
**kwargs,
791
) -> list:
792
# Filter examples with at least 2 conversations
793
filtered_data = self.data.filter(lambda x: len(x["conversations"]) >= 2)
794
sampled_requests = []
795
dynamic_output = output_len is None
796
+ ind = 0
797
798
for item in filtered_data:
799
if len(sampled_requests) >= num_requests:
@@ -785,9 +821,13 @@ class ConversationDataset(HuggingFaceDataset):
821
prompt_len=prompt_len,
822
expected_output_len=output_len,
823
multi_modal_data=mm_content,
824
+ request_id=request_id_prefix + str(ind),
825
)
826
)
790
- self.maybe_oversample_requests(sampled_requests, num_requests)
827
+ ind += 1
828
+ self.maybe_oversample_requests(
829
+ sampled_requests, num_requests, request_id_prefix
830
+ )
831
return sampled_requests
832
833
@@ -814,11 +854,12 @@ class VisionArenaDataset(HuggingFaceDataset):
854
num_requests: int,
855
output_len: Optional[int] = None,
856
enable_multimodal_chat: bool = False,
857
+ request_id_prefix: str = "",
858
**kwargs,
859
) -> list:
860
output_len = output_len if output_len is not None else self.DEFAULT_OUTPUT_LEN
861
sampled_requests = []
821
- for item in self.data:
862
+ for i, item in enumerate(self.data):
863
if len(sampled_requests) >= num_requests:
864
break
865
parser_fn = self.SUPPORTED_DATASET_PATHS.get(self.dataset_path)
@@ -838,9 +879,12 @@ class VisionArenaDataset(HuggingFaceDataset):
879
prompt_len=prompt_len,
880
expected_output_len=output_len,
881
multi_modal_data=mm_content,
882
+ request_id=request_id_prefix + str(i),
883
)
884
)
843
- self.maybe_oversample_requests(sampled_requests, num_requests)
885
+ self.maybe_oversample_requests(
886
+ sampled_requests, num_requests, request_id_prefix
887
+ )
888
return sampled_requests
889
890
@@ -870,11 +914,12 @@ class InstructCoderDataset(HuggingFaceDataset):
914
num_requests: int,
915
output_len: Optional[int] = None,
916
enable_multimodal_chat: bool = False,
917
+ request_id_prefix: str = "",
918
**kwargs,
919
) -> list:
920
output_len = output_len if output_len is not None else self.DEFAULT_OUTPUT_LEN
921
sampled_requests = []
877
- for item in self.data:
922
+ for i, item in enumerate(self.data):
923
if len(sampled_requests) >= num_requests:
924
break
925
prompt = f"{item['input']}\n\n{item['instruction']} Just output \
@@ -892,9 +937,12 @@ class InstructCoderDataset(HuggingFaceDataset):
937
prompt=prompt,
938
prompt_len=prompt_len,
939
expected_output_len=output_len,
940
+ request_id=request_id_prefix + str(i),
941
)
942
)
897
- self.maybe_oversample_requests(sampled_requests, num_requests)
943
+ self.maybe_oversample_requests(
944
+ sampled_requests, num_requests, request_id_prefix
945
+ )
946
return sampled_requests
947
948
@@ -924,12 +972,13 @@ class MTBenchDataset(HuggingFaceDataset):
972
num_requests: int,
973
output_len: Optional[int] = None,
974
enable_multimodal_chat: bool = False,
975
+ request_id_prefix: str = "",
976
**kwargs,
977
) -> list:
978
output_len = output_len if output_len is not None else self.DEFAULT_OUTPUT_LEN
979
sampled_requests = []
980
932
- for item in self.data:
981
+ for i, item in enumerate(self.data):
982
if len(sampled_requests) >= num_requests:
983
break
984
prompt = item["turns"][0]
@@ -947,9 +996,12 @@ class MTBenchDataset(HuggingFaceDataset):
996
prompt=prompt,
997
prompt_len=prompt_len,
998
expected_output_len=output_len,
999
+ request_id=request_id_prefix + str(i),
1000
)
1001
)
952
- self.maybe_oversample_requests(sampled_requests, num_requests)
1002
+ self.maybe_oversample_requests(
1003
+ sampled_requests, num_requests, request_id_prefix
1004
+ )
1005
return sampled_requests
1006
1007
@@ -974,10 +1026,12 @@ class AIMODataset(HuggingFaceDataset):
1026
tokenizer: PreTrainedTokenizerBase,
1027
num_requests: int,
1028
output_len: Optional[int] = None,
1029
+ request_id_prefix: str = "",
1030
**kwargs,
1031
) -> list:
1032
sampled_requests = []
1033
dynamic_output = output_len is None
1034
+ ind = 0
1035
1036
for item in self.data:
1037
if len(sampled_requests) >= num_requests:
@@ -1000,9 +1054,13 @@ class AIMODataset(HuggingFaceDataset):
1054
prompt_len=prompt_len,
1055
expected_output_len=output_len,
1056
multi_modal_data=None,
1057
+ request_id=request_id_prefix + str(ind),
1058
)
1059
)
1005
- self.maybe_oversample_requests(sampled_requests, num_requests)
1060
+ ind += 1
1061
+ self.maybe_oversample_requests(
1062
+ sampled_requests, num_requests, request_id_prefix
1063
+ )
1064
return sampled_requests
1065
1066
@@ -1072,12 +1130,18 @@ class NextEditPredictionDataset(HuggingFaceDataset):
1130
"zed-industries/zeta": _format_zeta_prompt,
1131
}
1132
1075
- def sample(self, tokenizer: PreTrainedTokenizerBase, num_requests: int, **kwargs):
1133
+ def sample(
1134
+ self,
1135
+ tokenizer: PreTrainedTokenizerBase,
1136
+ num_requests: int,
1137
+ request_id_prefix: str = "",
1138
+ **kwargs,
1139
+ ):
1140
formatting_prompt_func = self.MAPPING_PROMPT_FUNCS.get(self.dataset_path)
1141
if formatting_prompt_func is None:
1142
raise ValueError(f"Unsupported dataset path: {self.dataset_path}")
1143
samples = []
1080
- for sample in self.data:
1144
+ for i, sample in enumerate(self.data):
1145
sample = formatting_prompt_func(sample)
1146
samples.append(
1147
SampleRequest(
@@ -1086,11 +1150,12 @@ class NextEditPredictionDataset(HuggingFaceDataset):
1150
expected_output_len=len(
1151
tokenizer(sample["expected_output"]).input_ids
1152
),
1153
+ request_id=request_id_prefix + str(i),
1154
)
1155
)
1156
if len(samples) >= num_requests:
1157
break
1093
- self.maybe_oversample_requests(samples, num_requests)
1158
+ self.maybe_oversample_requests(samples, num_requests, request_id_prefix)
1159
return samples
1160
1161
@@ -1139,6 +1204,7 @@ class ASRDataset(HuggingFaceDataset):
1204
tokenizer: PreTrainedTokenizerBase,
1205
num_requests: int,
1206
output_len: Optional[int] = None,
1207
+ request_id_prefix: str = "",
1208
**kwargs,
1209
) -> list:
1210
import librosa
@@ -1148,6 +1214,7 @@ class ASRDataset(HuggingFaceDataset):
1214
prompt_len = len(tokenizer(prompt).input_ids)
1215
sampled_requests = []
1216
skipped = 0
1217
+ ind = 0
1218
for item in self.data:
1219
if len(sampled_requests) >= num_requests:
1220
break
@@ -1166,8 +1233,10 @@ class ASRDataset(HuggingFaceDataset):
1233
prompt_len=prompt_len,
1234
expected_output_len=output_len,
1235
multi_modal_data=mm_content,
1236
+ request_id=request_id_prefix + str(ind),
1237
)
1238
)
1239
+ ind += 1
1240
if skipped:
1241
logger.warning(
1242
"%d samples discarded from dataset due to"
@@ -1175,5 +1244,7 @@ class ASRDataset(HuggingFaceDataset):
1244
" what Whisper supports.",
1245
skipped,
1246
)
1178
- self.maybe_oversample_requests(sampled_requests, num_requests)
1247
+ self.maybe_oversample_requests(
1248
+ sampled_requests, num_requests, request_id_prefix
1249
+ )
1250
return sampled_requests
benchmarks/benchmark_serving.py
+21
-2
@@ -375,11 +375,12 @@ async def benchmark(
375
rps_change_events.append({"rps": rps_val, "timestamp": timestamp})
376
last_int_rps = current_int_rps
377
378
- prompt, prompt_len, output_len, mm_content = (
378
+ prompt, prompt_len, output_len, mm_content, request_id = (
379
request.prompt,
380
request.prompt_len,
381
request.expected_output_len,
382
request.multi_modal_data,
383
+ request.request_id,
384
)
385
req_model_id, req_model_name = model_id, model_name
386
if lora_modules:
@@ -397,6 +398,7 @@ async def benchmark(
398
multi_modal_content=mm_content,
399
ignore_eos=ignore_eos,
400
extra_body=extra_body,
401
+ request_id=request_id,
402
)
403
task = limited_request_func(request_func_input=request_func_input, pbar=pbar)
404
tasks.append(asyncio.create_task(task))
@@ -665,6 +667,7 @@ def main(args: argparse.Namespace):
667
tokenizer=tokenizer,
668
output_len=args.custom_output_len,
669
skip_chat_template=args.custom_skip_chat_template,
670
+ request_id_prefix=args.request_id_prefix,
671
)
672
673
elif args.dataset_name == "sonnet":
@@ -678,6 +681,7 @@ def main(args: argparse.Namespace):
681
prefix_len=args.sonnet_prefix_len,
682
tokenizer=tokenizer,
683
return_prompt_formatted=False,
684
+ request_id_prefix=args.request_id_prefix,
685
)
686
else:
687
assert tokenizer.chat_template or tokenizer.default_chat_template, (
@@ -690,6 +694,7 @@ def main(args: argparse.Namespace):
694
prefix_len=args.sonnet_prefix_len,
695
tokenizer=tokenizer,
696
return_prompt_formatted=True,
697
+ request_id_prefix=args.request_id_prefix,
698
)
699
700
elif args.dataset_name == "hf":
@@ -751,6 +756,7 @@ def main(args: argparse.Namespace):
756
num_requests=args.num_prompts,
757
tokenizer=tokenizer,
758
output_len=args.hf_output_len,
759
+ request_id_prefix=args.request_id_prefix,
760
)
761
762
else:
@@ -762,10 +768,15 @@ def main(args: argparse.Namespace):
768
tokenizer=tokenizer,
769
num_requests=args.num_prompts,
770
output_len=args.sharegpt_output_len,
771
+ request_id_prefix=args.request_id_prefix,
772
),
773
"burstgpt": lambda: BurstGPTDataset(
774
random_seed=args.seed, dataset_path=args.dataset_path
768
- ).sample(tokenizer=tokenizer, num_requests=args.num_prompts),
775
+ ).sample(
776
+ tokenizer=tokenizer,
777
+ num_requests=args.num_prompts,
778
+ request_id_prefix=args.request_id_prefix,
779
+ ),
780
"random": lambda: RandomDataset(dataset_path=args.dataset_path).sample(
781
tokenizer=tokenizer,
782
num_requests=args.num_prompts,
@@ -773,6 +784,7 @@ def main(args: argparse.Namespace):
784
input_len=args.random_input_len,
785
output_len=args.random_output_len,
786
range_ratio=args.random_range_ratio,
787
+ request_id_prefix=args.request_id_prefix,
788
),
789
}
790
@@ -1118,6 +1130,13 @@ def create_argument_parser():
1130
"goodput, refer to DistServe paper: https://arxiv.org/pdf/2401.09670 "
1131
"and the blog: https://hao-ai-lab.github.io/blogs/distserve",
1132
)
1133
+ parser.add_argument(
1134
+ "--request-id-prefix",
1135
+ type=str,
1136
+ required=False,
1137
+ default="benchmark-serving",
1138
+ help="Specify the prefix of request id.",
1139
+ )
1140
1141
# group for dataset specific arguments
1142
custom_group = parser.add_argument_group("custom dataset options")
vllm/benchmarks/datasets.py
+92
-21
@@ -18,6 +18,7 @@ import logging
18
import random
19
from abc import ABC, abstractmethod
20
from collections.abc import Mapping
21
+from copy import deepcopy
22
from dataclasses import dataclass
23
from functools import cache
24
from io import BytesIO
@@ -76,6 +77,7 @@ class SampleRequest:
77
Union[MultiModalDataDict, dict, list[dict]]
78
] = None
79
lora_request: Optional[LoRARequest] = None
80
+ request_id: Optional[str] = None
81
82
83
# -----------------------------------------------------------------------------
@@ -183,7 +185,8 @@ class BenchmarkDataset(ABC):
185
186
@abstractmethod
187
def sample(self, tokenizer: PreTrainedTokenizerBase,
186
- num_requests: int) -> list[SampleRequest]:
188
+ num_requests: int,
189
+ request_id_prefix: str = "") -> list[SampleRequest]:
190
"""
191
Abstract method to generate sample requests from the dataset.
192
@@ -194,6 +197,8 @@ class BenchmarkDataset(ABC):
197
tokenizer (PreTrainedTokenizerBase): The tokenizer to be used
198
for processing the dataset's text.
199
num_requests (int): The number of sample requests to generate.
200
+ request_id_prefix (str) The prefix of request_id.
201
+
202
203
Returns:
204
list[SampleRequest]: A list of sample requests generated from the
@@ -201,8 +206,12 @@ class BenchmarkDataset(ABC):
206
"""
207
raise NotImplementedError("sample must be implemented in subclasses.")
208
204
- def maybe_oversample_requests(self, requests: list[SampleRequest],
205
- num_requests: int) -> None:
209
+ def maybe_oversample_requests(
210
+ self,
211
+ requests: list[SampleRequest],
212
+ num_requests: int,
213
+ request_id_prefix: str = "",
214
+ ) -> None:
215
"""
216
Oversamples the list of requests if its size is less than the desired
217
number.
@@ -211,11 +220,17 @@ class BenchmarkDataset(ABC):
220
requests (List[SampleRequest]): The current list of sampled
221
requests.
222
num_requests (int): The target number of requests.
223
+ request_id_prefix (str) The prefix of the request ids.
224
+
225
"""
226
if len(requests) < num_requests:
227
random.seed(self.random_seed)
217
- additional = random.choices(requests,
218
- k=num_requests - len(requests))
228
+ additional = deepcopy(
229
+ random.choices(requests, k=num_requests - len(requests))
230
+ )
231
+ for i in range(len(additional)):
232
+ req = additional[i]
233
+ req.request_id = request_id_prefix + str(len(requests) + i)
234
requests.extend(additional)
235
logger.info("Oversampled requests to reach %d total samples.",
236
num_requests)
@@ -334,6 +349,7 @@ class RandomDataset(BenchmarkDataset):
349
range_ratio: float = DEFAULT_RANGE_RATIO,
350
input_len: int = DEFAULT_INPUT_LEN,
351
output_len: int = DEFAULT_OUTPUT_LEN,
352
+ request_id_prefix: str = "",
353
**kwargs,
354
) -> list[SampleRequest]:
355
# Enforce range_ratio < 1
@@ -391,6 +407,7 @@ class RandomDataset(BenchmarkDataset):
407
prompt=prompt,
408
prompt_len=total_input_len,
409
expected_output_len=int(output_lens[i]),
410
+ request_id=request_id_prefix + str(i),
411
))
412
return requests
413
@@ -432,9 +449,11 @@ class ShareGPTDataset(BenchmarkDataset):
449
max_loras: Optional[int] = None,
450
output_len: Optional[int] = None,
451
enable_multimodal_chat: bool = False,
452
+ request_id_prefix: str = "",
453
**kwargs,
454
) -> list:
455
samples: list = []
456
+ ind = 0
457
for entry in self.data:
458
if len(samples) >= num_requests:
459
break
@@ -470,8 +489,10 @@ class ShareGPTDataset(BenchmarkDataset):
489
expected_output_len=new_output_len,
490
lora_request=lora_request,
491
multi_modal_data=mm_content,
492
+ request_id=request_id_prefix + str(ind),
493
))
474
- self.maybe_oversample_requests(samples, num_requests)
494
+ ind += 1
495
+ self.maybe_oversample_requests(samples, num_requests, request_id_prefix)
496
return samples
497
498
@@ -647,6 +668,7 @@ def get_samples(args, tokenizer) -> list[SampleRequest]:
668
tokenizer=tokenizer,
669
output_len=args.custom_output_len,
670
skip_chat_template=args.custom_skip_chat_template,
671
+ request_id_prefix=args.request_id_prefix,
672
)
673
674
elif args.dataset_name == "sonnet":
@@ -660,6 +682,7 @@ def get_samples(args, tokenizer) -> list[SampleRequest]:
682
prefix_len=args.sonnet_prefix_len,
683
tokenizer=tokenizer,
684
return_prompt_formatted=False,
685
+ request_id_prefix=args.request_id_prefix,
686
)
687
else:
688
assert tokenizer.chat_template or tokenizer.default_chat_template, (
@@ -671,6 +694,7 @@ def get_samples(args, tokenizer) -> list[SampleRequest]:
694
prefix_len=args.sonnet_prefix_len,
695
tokenizer=tokenizer,
696
return_prompt_formatted=True,
697
+ request_id_prefix=args.request_id_prefix,
698
)
699
700
elif args.dataset_name == "hf":
@@ -730,6 +754,7 @@ def get_samples(args, tokenizer) -> list[SampleRequest]:
754
num_requests=args.num_prompts,
755
tokenizer=tokenizer,
756
output_len=args.hf_output_len,
757
+ request_id_prefix=args.request_id_prefix,
758
)
759
760
else:
@@ -741,11 +766,13 @@ def get_samples(args, tokenizer) -> list[SampleRequest]:
766
tokenizer=tokenizer,
767
num_requests=args.num_prompts,
768
output_len=args.sharegpt_output_len,
769
+ request_id_prefix=args.request_id_prefix,
770
),
771
"burstgpt":
772
lambda: BurstGPTDataset(random_seed=args.seed,
773
dataset_path=args.dataset_path).
748
- sample(tokenizer=tokenizer, num_requests=args.num_prompts),
774
+ sample(tokenizer=tokenizer, num_requests=args.num_prompts,
775
+ request_id_prefix=args.request_id_prefix,),
776
"random":
777
lambda: RandomDataset(random_seed=args.seed,
778
dataset_path=args.dataset_path).sample(
@@ -755,6 +782,7 @@ def get_samples(args, tokenizer) -> list[SampleRequest]:
782
input_len=args.random_input_len,
783
output_len=args.random_output_len,
784
range_ratio=args.random_range_ratio,
785
+ request_id_prefix=args.request_id_prefix,
786
),
787
"prefix_repetition":
788
lambda: PrefixRepetitionRandomDataset(
@@ -766,6 +794,7 @@ def get_samples(args, tokenizer) -> list[SampleRequest]:
794
suffix_len=args.prefix_repetition_suffix_len,
795
num_prefixes=args.prefix_repetition_num_prefixes,
796
output_len=args.prefix_repetition_output_len,
797
+ request_id_prefix=args.request_id_prefix,
798
),
799
}
800
@@ -839,10 +868,11 @@ class CustomDataset(BenchmarkDataset):
868
output_len: Optional[int] = None,
869
enable_multimodal_chat: bool = False,
870
skip_chat_template: bool = False,
871
+ request_id_prefix: str = "",
872
**kwargs,
873
) -> list:
874
sampled_requests = []
845
- for item in self.data:
875
+ for i, item in enumerate(self.data):
876
if len(sampled_requests) >= num_requests:
877
break
878
prompt = item["prompt"]
@@ -864,8 +894,10 @@ class CustomDataset(BenchmarkDataset):
894
prompt=prompt,
895
prompt_len=prompt_len,
896
expected_output_len=output_len,
897
+ request_id=request_id_prefix + str(i),
898
))
868
- self.maybe_oversample_requests(sampled_requests, num_requests)
899
+ self.maybe_oversample_requests(sampled_requests, num_requests,
900
+ request_id_prefix)
901
902
return sampled_requests
903
@@ -909,6 +941,7 @@ class SonnetDataset(BenchmarkDataset):
941
input_len: int = DEFAULT_INPUT_LEN,
942
output_len: int = DEFAULT_OUTPUT_LEN,
943
return_prompt_formatted: bool = False,
944
+ request_id_prefix: str = "",
945
**kwargs,
946
) -> list:
947
# Calculate average token length for a poem line.
@@ -934,6 +967,7 @@ class SonnetDataset(BenchmarkDataset):
967
prefix_lines = self.data[:num_prefix_lines]
968
969
samples = []
970
+ ind = 0
971
while len(samples) < num_requests:
972
extra_lines = random.choices(self.data,
973
k=num_input_lines - num_prefix_lines)
@@ -949,7 +983,9 @@ class SonnetDataset(BenchmarkDataset):
983
if return_prompt_formatted else prompt,
984
prompt_len=prompt_len,
985
expected_output_len=output_len,
986
+ request_id=request_id_prefix + str(ind),
987
))
988
+ ind += 1
989
return samples
990
991
@@ -1000,6 +1036,7 @@ class BurstGPTDataset(BenchmarkDataset):
1036
num_requests: int,
1037
max_loras: Optional[int] = None,
1038
lora_path: Optional[str] = None,
1039
+ request_id_prefix: str = "",
1040
**kwargs,
1041
) -> list[SampleRequest]:
1042
samples = []
@@ -1020,6 +1057,7 @@ class BurstGPTDataset(BenchmarkDataset):
1057
prompt_len=input_len,
1058
expected_output_len=output_len,
1059
lora_request=lora_req,
1060
+ request_id=request_id_prefix + str(i),
1061
))
1062
return samples
1063
@@ -1075,11 +1113,13 @@ class ConversationDataset(HuggingFaceDataset):
1113
num_requests: int,
1114
output_len: Optional[int] = None,
1115
enable_multimodal_chat: bool = False,
1116
+ request_id_prefix: str = "",
1117
**kwargs) -> list:
1118
# Filter examples with at least 2 conversations
1119
filtered_data = self.data.filter(
1120
lambda x: len(x["conversations"]) >= 2)
1121
sampled_requests = []
1122
+ ind = 0
1123
dynamic_output = output_len is None
1124
1125
for item in filtered_data:
@@ -1111,8 +1151,11 @@ class ConversationDataset(HuggingFaceDataset):
1151
prompt_len=prompt_len,
1152
expected_output_len=output_len,
1153
multi_modal_data=mm_content,
1154
+ request_id=request_id_prefix + str(ind),
1155
))
1115
- self.maybe_oversample_requests(sampled_requests, num_requests)
1156
+ ind += 1
1157
+ self.maybe_oversample_requests(sampled_requests, num_requests,
1158
+ request_id_prefix)
1159
return sampled_requests
1160
1161
@@ -1141,12 +1184,13 @@ class VisionArenaDataset(HuggingFaceDataset):
1184
num_requests: int,
1185
output_len: Optional[int] = None,
1186
enable_multimodal_chat: bool = False,
1187
+ request_id_prefix: str = "",
1188
**kwargs,
1189
) -> list:
1190
output_len = (output_len
1191
if output_len is not None else self.DEFAULT_OUTPUT_LEN)
1192
sampled_requests = []
1149
- for item in self.data:
1193
+ for i, item in enumerate(self.data):
1194
if len(sampled_requests) >= num_requests:
1195
break
1196
parser_fn = self.SUPPORTED_DATASET_PATHS.get(self.dataset_path)
@@ -1168,8 +1212,10 @@ class VisionArenaDataset(HuggingFaceDataset):
1212
prompt_len=prompt_len,
1213
expected_output_len=output_len,
1214
multi_modal_data=mm_content,
1215
+ request_id=request_id_prefix + str(i),
1216
))
1172
- self.maybe_oversample_requests(sampled_requests, num_requests)
1217
+ self.maybe_oversample_requests(sampled_requests, num_requests,
1218
+ request_id_prefix)
1219
return sampled_requests
1220
1221
@@ -1198,11 +1244,12 @@ class InstructCoderDataset(HuggingFaceDataset):
1244
num_requests: int,
1245
output_len: Optional[int] = None,
1246
enable_multimodal_chat: bool = False,
1247
+ request_id_prefix: str = "",
1248
**kwargs) -> list:
1249
output_len = (output_len
1250
if output_len is not None else self.DEFAULT_OUTPUT_LEN)
1251
sampled_requests = []
1205
- for item in self.data:
1252
+ for i, item in enumerate(self.data):
1253
if len(sampled_requests) >= num_requests:
1254
break
1255
prompt = f"{item['input']}\n\n{item['instruction']} Just output \
@@ -1224,8 +1271,10 @@ class InstructCoderDataset(HuggingFaceDataset):
1271
prompt=prompt,
1272
prompt_len=prompt_len,
1273
expected_output_len=output_len,
1274
+ request_id=request_id_prefix + str(i),
1275
))
1228
- self.maybe_oversample_requests(sampled_requests, num_requests)
1276
+ self.maybe_oversample_requests(sampled_requests, num_requests,
1277
+ request_id_prefix)
1278
return sampled_requests
1279
1280
@@ -1255,13 +1304,14 @@ class MTBenchDataset(HuggingFaceDataset):
1304
num_requests: int,
1305
output_len: Optional[int] = None,
1306
enable_multimodal_chat: bool = False,
1307
+ request_id_prefix: str = "",
1308
**kwargs,
1309
) -> list:
1310
output_len = (output_len
1311
if output_len is not None else self.DEFAULT_OUTPUT_LEN)
1312
sampled_requests = []
1313
1264
- for item in self.data:
1314
+ for i, item in enumerate(self.data):
1315
if len(sampled_requests) >= num_requests:
1316
break
1317
prompt = item["turns"][0]
@@ -1282,8 +1332,10 @@ class MTBenchDataset(HuggingFaceDataset):
1332
prompt=prompt,
1333
prompt_len=prompt_len,
1334
expected_output_len=output_len,
1335
+ request_id=request_id_prefix + str(i),
1336
))
1286
- self.maybe_oversample_requests(sampled_requests, num_requests)
1337
+ self.maybe_oversample_requests(sampled_requests, num_requests,
1338
+ request_id_prefix)
1339
return sampled_requests
1340
1341
@@ -1305,8 +1357,10 @@ class AIMODataset(HuggingFaceDataset):
1357
tokenizer: PreTrainedTokenizerBase,
1358
num_requests: int,
1359
output_len: Optional[int] = None,
1360
+ request_id_prefix: str = "",
1361
**kwargs) -> list:
1362
sampled_requests = []
1363
+ ind = 0
1364
dynamic_output = output_len is None
1365
1366
for item in self.data:
@@ -1331,8 +1385,12 @@ class AIMODataset(HuggingFaceDataset):
1385
prompt_len=prompt_len,
1386
expected_output_len=output_len,
1387
multi_modal_data=None,
1388
+ request_id=request_id_prefix + str(ind),
1389
+
1390
))
1335
- self.maybe_oversample_requests(sampled_requests, num_requests)
1391
+ ind += 1
1392
+ self.maybe_oversample_requests(sampled_requests, num_requests,
1393
+ request_id_prefix)
1394
return sampled_requests
1395
1396
@@ -1403,13 +1461,14 @@ class NextEditPredictionDataset(HuggingFaceDataset):
1461
}
1462
1463
def sample(self, tokenizer: PreTrainedTokenizerBase, num_requests: int,
1464
+ request_id_prefix: str = "",
1465
**kwargs):
1466
formatting_prompt_func = self.MAPPING_PROMPT_FUNCS.get(
1467
self.dataset_path)
1468
if formatting_prompt_func is None:
1469
raise ValueError(f"Unsupported dataset path: {self.dataset_path}")
1470
samples = []
1412
- for sample in self.data:
1471
+ for i, sample in enumerate(self.data):
1472
sample = formatting_prompt_func(sample)
1473
samples.append(
1474
SampleRequest(
@@ -1417,10 +1476,11 @@ class NextEditPredictionDataset(HuggingFaceDataset):
1476
prompt_len=len(tokenizer(sample["prompt"]).input_ids),
1477
expected_output_len=len(
1478
tokenizer(sample["expected_output"]).input_ids),
1479
+ request_id=request_id_prefix + str(i),
1480
))
1481
if len(samples) >= num_requests:
1482
break
1423
- self.maybe_oversample_requests(samples, num_requests)
1483
+ self.maybe_oversample_requests(samples, num_requests, request_id_prefix)
1484
return samples
1485
1486
@@ -1470,6 +1530,7 @@ class ASRDataset(HuggingFaceDataset):
1530
tokenizer: PreTrainedTokenizerBase,
1531
num_requests: int,
1532
output_len: Optional[int] = None,
1533
+ request_id_prefix: str = "",
1534
**kwargs,
1535
) -> list:
1536
output_len = (output_len
@@ -1477,6 +1538,7 @@ class ASRDataset(HuggingFaceDataset):
1538
prompt = ASRDataset.TRANSCRIPTION_PREAMBLE
1539
prompt_len = len(tokenizer(prompt).input_ids)
1540
sampled_requests = []
1541
+ ind = 0
1542
skipped = 0
1543
for item in self.data:
1544
if len(sampled_requests) >= num_requests:
@@ -1496,7 +1558,9 @@ class ASRDataset(HuggingFaceDataset):
1558
prompt_len=prompt_len,
1559
expected_output_len=output_len,
1560
multi_modal_data=mm_content,
1561
+ request_id=request_id_prefix + str(ind),
1562
))
1563
+ ind += 1
1564
if skipped:
1565
logger.warning(
1566
"%d samples discarded from dataset due to"
@@ -1504,7 +1568,8 @@ class ASRDataset(HuggingFaceDataset):
1568
" what Whisper supports.",
1569
skipped,
1570
)
1507
- self.maybe_oversample_requests(sampled_requests, num_requests)
1571
+ self.maybe_oversample_requests(sampled_requests, num_requests,
1572
+ request_id_prefix)
1573
return sampled_requests
1574
1575
@@ -1541,11 +1606,13 @@ class MLPerfDataset(HuggingFaceDataset):
1606
tokenizer: PreTrainedTokenizerBase,
1607
num_requests: int,
1608
output_len: Optional[int] = None,
1609
+ request_id_prefix: str = "",
1610
**kwargs,
1611
) -> list[SampleRequest]:
1612
# Force dynamic output length based on reference completion.
1613
dynamic_output = output_len is None
1614
sampled_requests: list[SampleRequest] = []
1615
+ ind = 0
1616
1617
for item in self.data:
1618
if len(sampled_requests) >= num_requests:
@@ -1580,10 +1647,13 @@ class MLPerfDataset(HuggingFaceDataset):
1647
prompt=prompt_formatted,
1648
prompt_len=prompt_len,
1649
expected_output_len=expected_output_len,
1650
+ request_id=request_id_prefix + str(ind),
1651
)
1652
)
1653
+ ind += 1
1654
1586
- self.maybe_oversample_requests(sampled_requests, num_requests)
1655
+ self.maybe_oversample_requests(sampled_requests, num_requests,
1656
+ request_id_prefix)
1657
return sampled_requests
1658
1659
@@ -1616,6 +1686,7 @@ class PrefixRepetitionRandomDataset(BenchmarkDataset):
1686
suffix_len: int = DEFAULT_SUFFIX_LEN,
1687
num_prefixes: int = DEFAULT_NUM_PREFIXES,
1688
output_len: int = DEFAULT_OUTPUT_LEN,
1689
+ request_id_prefix: str = "",
1690
**kwargs,
1691
) -> list[SampleRequest]:
1692
vocab_size = tokenizer.vocab_size
vllm/benchmarks/lib/endpoint_request_func.py
+7
@@ -31,6 +31,7 @@ class RequestFuncInput:
31
multi_modal_content: Optional[dict | list[dict]] = None
32
ignore_eos: bool = False
33
language: Optional[str] = None
34
+ request_id: Optional[str] = None
35
36
37
@dataclass
@@ -87,6 +88,8 @@ async def async_request_openai_completions(
88
headers = {
89
"Authorization": f"Bearer {os.environ.get('OPENAI_API_KEY')}"
90
}
91
+ if request_func_input.request_id:
92
+ headers["x-request-id"] = request_func_input.request_id
93
94
output = RequestFuncOutput()
95
output.prompt_len = request_func_input.prompt_len
@@ -210,6 +213,8 @@ async def async_request_openai_chat_completions(
213
"Content-Type": "application/json",
214
"Authorization": f"Bearer {os.environ.get('OPENAI_API_KEY')}",
215
}
216
+ if request_func_input.request_id:
217
+ headers["x-request-id"] = request_func_input.request_id
218
219
output = RequestFuncOutput()
220
output.prompt_len = request_func_input.prompt_len
@@ -311,6 +316,8 @@ async def async_request_openai_audio(
316
headers = {
317
"Authorization": f"Bearer {os.environ.get('OPENAI_API_KEY')}",
318
}
319
+ if request_func_input.request_id:
320
+ headers["x-request-id"] = request_func_input.request_id
321
322
# Send audio file
323
def to_bytes(y, sr):
vllm/benchmarks/serve.py
+12
-2
@@ -478,11 +478,12 @@ async def benchmark(
478
"timestamp": timestamp
479
})
480
last_int_rps = current_int_rps
481
- prompt, prompt_len, output_len, mm_content = (
481
+ prompt, prompt_len, output_len, mm_content, request_id = (
482
request.prompt,
483
request.prompt_len,
484
request.expected_output_len,
485
request.multi_modal_data,
486
+ request.request_id,
487
)
488
req_model_id, req_model_name = model_id, model_name
489
if lora_modules:
@@ -498,7 +499,8 @@ async def benchmark(
499
logprobs=logprobs,
500
multi_modal_content=mm_content,
501
ignore_eos=ignore_eos,
501
- extra_body=extra_body)
502
+ extra_body=extra_body,
503
+ request_id=request_id,)
504
tasks.append(
505
asyncio.create_task(
506
limited_request_func(request_func_input=request_func_input,
@@ -865,6 +867,14 @@ def add_cli_args(parser: argparse.ArgumentParser):
867
"goodput, refer to DistServe paper: https://arxiv.org/pdf/2401.09670 "
868
"and the blog: https://hao-ai-lab.github.io/blogs/distserve",
869
)
870
+ parser.add_argument(
871
+ "--request-id-prefix",
872
+ type=str,
873
+ required=False,
874
+ default="benchmark-serving",
875
+ help="Specify the prefix of request id.",
876
+ )
877
+
878
879
sampling_group = parser.add_argument_group("sampling parameters")
880
sampling_group.add_argument(