Skip to content

Commit 52b6fe2

Browse files
committed
Fixes for issues with output overrides as well as errors in cooldown, warmup, and rampup logic
Signed-off-by: Mark Kurtz <mark.kurtz@neuralmagic.com>
1 parent b9aff65 commit 52b6fe2

File tree

17 files changed

+711
-558
lines changed

17 files changed

+711
-558
lines changed

src/guidellm/__main__.py

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,8 @@ def benchmark():
143143
)
144144
@click.option(
145145
"--rate",
146-
type=str,
147146
callback=cli_tools.parse_list_floats,
148-
multiple=False,
147+
multiple=True,
149148
default=BenchmarkGenerativeTextArgs.get_default("rate"),
150149
help=(
151150
"Benchmark rate(s) to test. Meaning depends on profile: "
@@ -255,36 +254,46 @@ def benchmark():
255254
)
256255
# Output configuration
257256
@click.option(
258-
"--output-path",
259-
type=click.Path(),
260-
default=BenchmarkGenerativeTextArgs.get_default("output_path"),
261-
help=(
262-
"Path to save output files. Can be a directory or file. "
263-
"If a file, saves that format; mismatched formats save to parent directory."
264-
),
257+
"--output-dir",
258+
type=click.Path(file_okay=False, dir_okay=True, path_type=Path),
259+
default=BenchmarkGenerativeTextArgs.get_default("output_dir"),
260+
help="The directory path to save file output types in",
265261
)
266262
@click.option(
267-
"--output-formats",
263+
"--outputs",
264+
callback=cli_tools.parse_list,
268265
multiple=True,
269-
type=str,
270-
default=BenchmarkGenerativeTextArgs.get_default("output_formats"),
271-
help="Output formats for results (e.g., console, json, html, csv).",
266+
default=BenchmarkGenerativeTextArgs.get_default("outputs"),
267+
help=(
268+
"The filename.ext for each of the outputs to create or the "
269+
"alises (json, csv, html) for the output files to create with "
270+
"their default file names (benchmark.[EXT])"
271+
),
272272
)
273273
@click.option(
274-
"--disable-console-outputs",
275-
is_flag=True,
276-
help="Disable console output.",
274+
"--output-path",
275+
type=click.Path(),
276+
default=None,
277+
help=(
278+
"Legacy parameter for the output path to save the output result to. "
279+
"Resolves to fill in output-dir and outputs based on input path."
280+
),
277281
)
278-
# Updates configuration
279282
@click.option(
280-
"--disable-progress",
283+
"--disable-console",
284+
"--disable-console-outputs", # legacy alias
285+
"disable_console",
281286
is_flag=True,
282-
help="Disable progress updates to the console.",
287+
help=(
288+
"Disable all outputs to the console (updates, interactive progress, results)."
289+
),
283290
)
284291
@click.option(
285-
"--display-scheduler-stats",
292+
"--disable-console-interactive",
293+
"--disable-progress", # legacy alias
294+
"disable_console_interactive",
286295
is_flag=True,
287-
help="Display scheduler process statistics.",
296+
help="Disable interactive console progress updates.",
288297
)
289298
# Aggregators configuration
290299
@click.option(
@@ -319,17 +328,11 @@ def benchmark():
319328
)
320329
@click.option(
321330
"--rampup",
331+
type=float,
322332
default=BenchmarkGenerativeTextArgs.get_default("rampup"),
323-
callback=cli_tools.parse_json,
324333
help=(
325-
"Rampup specification: int, float, or dict as string "
326-
"(json or key=value). "
327-
"Controls time to linearly ramp up requests. "
328-
"Only for Throughput/Concurrent strategies, "
329-
"not Synchronous/Rate-based. "
330-
"Numeric in (0, 1): percent of duration. "
331-
"Numeric >=1: duration in seconds. "
332-
"Advanced config: see TransientPhaseConfig schema."
334+
"The time, in seconds, to ramp up the request rate over. "
335+
"Only applicable for Throughput/Concurrent strategies"
333336
),
334337
)
335338
@click.option(
@@ -380,26 +383,29 @@ def benchmark():
380383
help="Maximum global error rate across all benchmarks.",
381384
)
382385
def run(**kwargs):
386+
# Handle remapping for request params
383387
request_type = kwargs.pop("request_type", None)
384388
request_formatter_kwargs = kwargs.pop("request_formatter_kwargs", None)
385389
kwargs["data_request_formatter"] = (
386390
request_type
387391
if not request_formatter_kwargs
388392
else {"request_type": request_type, **request_formatter_kwargs}
389393
)
390-
kwargs["data"] = cli_tools.format_list_arg(
391-
kwargs.get("data"), default=[], simplify_single=False
392-
)
393-
kwargs["data_args"] = cli_tools.format_list_arg(
394-
kwargs.get("data_args"), default=[], simplify_single=False
395-
)
396-
kwargs["rate"] = cli_tools.format_list_arg(
397-
kwargs.get("rate"), default=None, simplify_single=False
398-
)
399394

400-
disable_console_outputs = kwargs.pop("disable_console_outputs", False)
401-
display_scheduler_stats = kwargs.pop("display_scheduler_stats", False)
402-
disable_progress = kwargs.pop("disable_progress", False)
395+
# Handle output path remapping
396+
if (output_path := kwargs.pop("output_path", None)) is not None:
397+
path = Path(output_path)
398+
if path.is_dir():
399+
kwargs["output_dir"] = path
400+
else:
401+
kwargs["output_dir"] = path.parent
402+
kwargs["outputs"] = (path.suffix.lstrip(".").lower(),)
403+
404+
# Handle console options
405+
disable_console = kwargs.pop("disable_console", False)
406+
disable_console_interactive = (
407+
kwargs.pop("disable_console_interactive", False) or disable_console
408+
)
403409

404410
try:
405411
# Only set CLI args that differ from click defaults
@@ -421,13 +427,11 @@ def run(**kwargs):
421427
benchmark_generative_text(
422428
args=args,
423429
progress=(
424-
GenerativeConsoleBenchmarkerProgress(
425-
display_scheduler_stats=display_scheduler_stats
426-
)
427-
if not disable_progress
430+
GenerativeConsoleBenchmarkerProgress()
431+
if not disable_console_interactive
428432
else None
429433
),
430-
console=Console() if not disable_console_outputs else None,
434+
console=Console() if not disable_console else None,
431435
)
432436
)
433437

src/guidellm/benchmark/entrypoints.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121

2222
from guidellm.backends import Backend, BackendType
2323
from guidellm.benchmark.benchmarker import Benchmarker
24-
from guidellm.benchmark.outputs import GenerativeBenchmarkerOutput
24+
from guidellm.benchmark.outputs import (
25+
GenerativeBenchmarkerConsole,
26+
GenerativeBenchmarkerOutput,
27+
)
2528
from guidellm.benchmark.profiles import Profile, ProfileType
2629
from guidellm.benchmark.progress import GenerativeConsoleBenchmarkerProgress
2730
from guidellm.benchmark.schemas import (
@@ -313,7 +316,7 @@ async def resolve_profile(
313316
profile: StrategyType | ProfileType | Profile,
314317
rate: list[float] | None,
315318
random_seed: int,
316-
rampup: TransientPhaseConfig,
319+
rampup: float,
317320
constraints: MutableMapping[str, ConstraintInitializer | Any],
318321
max_seconds: int | float | None,
319322
max_requests: int | None,
@@ -332,7 +335,8 @@ async def resolve_profile(
332335
:param profile: Profile type identifier or pre-configured Profile instance
333336
:param rate: Request rate(s) for the benchmark execution
334337
:param random_seed: Seed for reproducible random operations
335-
:param rampup: Ramp-up phase configuration for the benchmark execution
338+
:param warmup: Warm-up phase configuration for the benchmark execution
339+
(used for ramp-up duration calculation)
336340
:param constraints: Dictionary of constraint initializers for benchmark limits
337341
:param max_seconds: Maximum duration in seconds for the benchmark
338342
:param max_requests: Maximum number of requests to process
@@ -358,24 +362,21 @@ async def resolve_profile(
358362
}.items():
359363
if val is not None:
360364
constraints[key] = val
361-
rampup_duration, _ = rampup.compute_limits(
362-
max_requests=max_requests, max_seconds=max_seconds
363-
)
364365

365366
if not isinstance(profile, Profile):
366367
profile = Profile.create(
367368
rate_type=profile,
368369
rate=rate,
369370
random_seed=random_seed,
370-
rampup_duration=rampup_duration or 0.0,
371+
rampup_duration=rampup,
371372
constraints={**constraints},
372373
)
373374
elif constraints:
374375
raise ValueError(
375376
"Constraints must be empty when providing a Profile instance. "
376377
f"Provided constraints: {constraints} ; provided profile: {profile}"
377378
)
378-
elif rampup_duration is not None:
379+
elif rampup > 0.0:
379380
raise ValueError(
380381
"Ramp-up duration must not be set when providing a Profile instance. "
381382
f"Provided rampup: {rampup} ; provided profile: {profile}"
@@ -392,15 +393,15 @@ async def resolve_profile(
392393

393394

394395
async def resolve_output_formats(
395-
output_formats: OutputFormatT,
396-
output_path: str | Path | None,
396+
outputs: list[str] | tuple[str],
397+
output_dir: str | Path | None,
397398
console: Console | None = None,
398399
) -> dict[str, GenerativeBenchmarkerOutput]:
399400
"""
400401
Resolve output format specifications into configured output handler instances.
401402
402-
:param output_formats: Specification of desired output formats
403-
:param output_path: Base path for output file generation, or None for default
403+
:param outputs: Specification of desired output files/types
404+
:param output_dir: Base path for output file generation, or None for default
404405
:param console: Console instance for progress reporting, or None
405406
:return: Dictionary mapping format names to configured output handler instances
406407
"""
@@ -409,7 +410,7 @@ async def resolve_output_formats(
409410
)
410411

411412
resolved = GenerativeBenchmarkerOutput.resolve(
412-
output_formats=output_formats, output_path=output_path
413+
outputs=outputs, output_dir=output_dir
413414
)
414415

415416
if console_step:
@@ -473,18 +474,16 @@ async def benchmark_generative_text(
473474
**(args.dataloader_kwargs or {}),
474475
)
475476

476-
rampup = TransientPhaseConfig.create_from_value(args.rampup)
477-
rampup.mode = "duration"
478477
warmup = TransientPhaseConfig.create_from_value(args.warmup)
479478
cooldown = TransientPhaseConfig.create_from_value(args.cooldown)
480479
if console:
481480
console.print_update(
482481
title="Resolved transient phase configurations",
483482
details="\n".join(
484483
[
485-
f"Rampup: {rampup}",
486484
f"Warmup: {warmup}",
487485
f"Cooldown: {cooldown}",
486+
f"Rampup (Throughput/Concurrent): {args.rampup}",
488487
]
489488
),
490489
status="success",
@@ -494,7 +493,7 @@ async def benchmark_generative_text(
494493
profile=args.profile,
495494
rate=args.rate,
496495
random_seed=args.random_seed,
497-
rampup=rampup,
496+
rampup=args.rampup,
498497
constraints=constraints,
499498
max_seconds=args.max_seconds,
500499
max_requests=args.max_requests,
@@ -504,9 +503,7 @@ async def benchmark_generative_text(
504503
console=console,
505504
)
506505
output_formats = await resolve_output_formats(
507-
output_formats=args.output_formats,
508-
output_path=args.output_path,
509-
console=console,
506+
outputs=args.outputs, output_dir=args.output_dir, console=console
510507
)
511508

512509
report = GenerativeBenchmarksReport(args=args)
@@ -541,6 +538,7 @@ async def benchmark_generative_text(
541538
output_format_results[key] = output_result
542539

543540
if console:
541+
await GenerativeBenchmarkerConsole(console=console).finalize(report)
544542
console.print("\n\n")
545543
console.print_update(
546544
title=(
@@ -581,7 +579,9 @@ async def reimport_benchmarks_report(
581579
)
582580

583581
resolved_output_formats = await resolve_output_formats(
584-
output_formats, output_path, console=console
582+
output_formats, # type: ignore[arg-type]
583+
output_path,
584+
console=console,
585585
)
586586
output_format_results = {}
587587
for key, output in resolved_output_formats.items():

0 commit comments

Comments
 (0)