Skip to content

guidellm.benchmark.benchmarker

Benchmark execution orchestration and lifecycle management.

Provides the core benchmarking engine that coordinates request scheduling, data aggregation, and result compilation across execution strategies and environments. The Benchmarker manages the complete benchmark lifecycle from request submission through result compilation while implementing thread-safe singleton operations for consistent state management across concurrent workflows.

Benchmarker

Bases: Generic[BenchmarkT, RequestT, ResponseT], ABC, ThreadSafeSingletonMixin

Orchestrates benchmark execution across scheduling strategies.

Coordinates benchmarking runs by managing request scheduling, metric aggregation, and result compilation. Implements a thread-safe singleton pattern to ensure consistent state management across concurrent operations while supporting multiple scheduling strategies and execution environments.

Source code in src/guidellm/benchmark/benchmarker.py
class Benchmarker(
    Generic[BenchmarkT, RequestT, ResponseT],
    ABC,
    ThreadSafeSingletonMixin,
):
    """
    Orchestrates benchmark execution across scheduling strategies.

    Coordinates benchmarking runs by managing request scheduling, metric aggregation,
    and result compilation. Implements a thread-safe singleton pattern to ensure
    consistent state management across concurrent operations while supporting multiple
    scheduling strategies and execution environments.
    """

    async def run(
        self,
        accumulator_class: type[BenchmarkAccumulatorT],
        benchmark_class: type[BenchmarkT],
        requests: DatasetIterT[RequestT],
        backend: BackendInterface[RequestT, ResponseT],
        profile: Profile,
        environment: Environment,
        warmup: TransientPhaseConfig,
        cooldown: TransientPhaseConfig,
        sample_size: int | None = None,
        prefer_response_metrics: bool = True,
        progress: (
            BenchmarkerProgress[BenchmarkAccumulatorT, BenchmarkT] | None
        ) = None,
    ) -> AsyncIterator[BenchmarkT]:
        """
        Execute benchmark runs across scheduling strategies in the profile.

        :param accumulator_class: Class for accumulating metrics during execution
        :param benchmark_class: Class for constructing final benchmark results
        :param requests: Request datasets to process across strategies
        :param backend: Backend interface for executing requests
        :param profile: Profile defining scheduling strategies and constraints
        :param environment: Environment for execution coordination
        :param warmup: Warmup phase configuration before benchmarking
        :param cooldown: Cooldown phase configuration after benchmarking
        :param sample_size: Maximum number of requests per status group
            (completed, errored, incomplete) to retain full data for.
            None keeps all, 0 strips all, N > 0 uses reservoir sampling.
        :param prefer_response_metrics: Whether to prefer response metrics over
            request metrics, defaults to True
        :param progress: Optional tracker for benchmark lifecycle events
        :yield: Compiled benchmark result for each strategy execution
        :raises Exception: If benchmark execution or compilation fails
        """
        with self.thread_lock:
            if progress:
                await progress.on_initialize(profile)

            run_id = str(uuid.uuid4())
            strategies_generator = profile.strategies_generator()
            strategy: SchedulingStrategy | None
            constraints: dict[str, Constraint] | None
            strategy, constraints = next(strategies_generator)

            while strategy is not None:
                if progress:
                    await progress.on_benchmark_start(strategy)

                config = BenchmarkConfig(
                    run_id=run_id,
                    run_index=len(profile.completed_strategies),
                    strategy=strategy,
                    constraints=(
                        {
                            key: InfoMixin.extract_from_obj(val)
                            for key, val in constraints.items()
                        }
                        if isinstance(constraints, dict)
                        else {"constraint": InfoMixin.extract_from_obj(constraints)}
                        if constraints
                        else {}
                    ),
                    sample_size=sample_size,
                    warmup=warmup,
                    cooldown=cooldown,
                    prefer_response_metrics=prefer_response_metrics,
                    profile=InfoMixin.extract_from_obj(profile),
                    requests=InfoMixin.extract_from_obj(requests),
                    backend=InfoMixin.extract_from_obj(backend),
                    environment=InfoMixin.extract_from_obj(environment),
                )
                accumulator = accumulator_class(config=config)
                scheduler_state = None
                scheduler: Scheduler[RequestT, ResponseT] = Scheduler()

                async for (
                    response,
                    request,
                    request_info,
                    scheduler_state,
                ) in scheduler.run(
                    requests=requests,
                    backend=backend,
                    strategy=strategy,
                    env=environment,
                    **constraints or {},
                ):
                    try:
                        accumulator.update_estimate(
                            response,
                            request,
                            request_info,
                            scheduler_state,
                        )
                        if progress:
                            await progress.on_benchmark_update(
                                accumulator, scheduler_state
                            )
                    except Exception as err:  # noqa: BLE001
                        logger.error(
                            "Error updating benchmark estimate/progress: {}", err
                        )

                benchmark = benchmark_class.compile(
                    accumulator=accumulator,
                    scheduler_state=scheduler_state,  # type: ignore[arg-type]
                )

                if progress:
                    await progress.on_benchmark_complete(benchmark)

                yield benchmark

                try:
                    strategy, constraints = strategies_generator.send(benchmark)
                except StopIteration:
                    strategy = None
                    constraints = None

            if progress:
                await progress.on_finalize()

run(accumulator_class, benchmark_class, requests, backend, profile, environment, warmup, cooldown, sample_size=None, prefer_response_metrics=True, progress=None) async

Execute benchmark runs across scheduling strategies in the profile.

:yield: Compiled benchmark result for each strategy execution

Parameters:

Name Type Description Default
accumulator_class type[BenchmarkAccumulatorT]

Class for accumulating metrics during execution

required
benchmark_class type[BenchmarkT]

Class for constructing final benchmark results

required
requests DatasetIterT[RequestT]

Request datasets to process across strategies

required
backend BackendInterface[RequestT, ResponseT]

Backend interface for executing requests

required
profile Profile

Profile defining scheduling strategies and constraints

required
environment Environment

Environment for execution coordination

required
warmup TransientPhaseConfig

Warmup phase configuration before benchmarking

required
cooldown TransientPhaseConfig

Cooldown phase configuration after benchmarking

required
sample_size int | None

Maximum number of requests per status group (completed, errored, incomplete) to retain full data for. None keeps all, 0 strips all, N > 0 uses reservoir sampling.

None
prefer_response_metrics bool

Whether to prefer response metrics over request metrics, defaults to True

True
progress BenchmarkerProgress[BenchmarkAccumulatorT, BenchmarkT] | None

Optional tracker for benchmark lifecycle events

None

Raises:

Type Description
Exception

If benchmark execution or compilation fails

Source code in src/guidellm/benchmark/benchmarker.py
async def run(
    self,
    accumulator_class: type[BenchmarkAccumulatorT],
    benchmark_class: type[BenchmarkT],
    requests: DatasetIterT[RequestT],
    backend: BackendInterface[RequestT, ResponseT],
    profile: Profile,
    environment: Environment,
    warmup: TransientPhaseConfig,
    cooldown: TransientPhaseConfig,
    sample_size: int | None = None,
    prefer_response_metrics: bool = True,
    progress: (
        BenchmarkerProgress[BenchmarkAccumulatorT, BenchmarkT] | None
    ) = None,
) -> AsyncIterator[BenchmarkT]:
    """
    Execute benchmark runs across scheduling strategies in the profile.

    :param accumulator_class: Class for accumulating metrics during execution
    :param benchmark_class: Class for constructing final benchmark results
    :param requests: Request datasets to process across strategies
    :param backend: Backend interface for executing requests
    :param profile: Profile defining scheduling strategies and constraints
    :param environment: Environment for execution coordination
    :param warmup: Warmup phase configuration before benchmarking
    :param cooldown: Cooldown phase configuration after benchmarking
    :param sample_size: Maximum number of requests per status group
        (completed, errored, incomplete) to retain full data for.
        None keeps all, 0 strips all, N > 0 uses reservoir sampling.
    :param prefer_response_metrics: Whether to prefer response metrics over
        request metrics, defaults to True
    :param progress: Optional tracker for benchmark lifecycle events
    :yield: Compiled benchmark result for each strategy execution
    :raises Exception: If benchmark execution or compilation fails
    """
    with self.thread_lock:
        if progress:
            await progress.on_initialize(profile)

        run_id = str(uuid.uuid4())
        strategies_generator = profile.strategies_generator()
        strategy: SchedulingStrategy | None
        constraints: dict[str, Constraint] | None
        strategy, constraints = next(strategies_generator)

        while strategy is not None:
            if progress:
                await progress.on_benchmark_start(strategy)

            config = BenchmarkConfig(
                run_id=run_id,
                run_index=len(profile.completed_strategies),
                strategy=strategy,
                constraints=(
                    {
                        key: InfoMixin.extract_from_obj(val)
                        for key, val in constraints.items()
                    }
                    if isinstance(constraints, dict)
                    else {"constraint": InfoMixin.extract_from_obj(constraints)}
                    if constraints
                    else {}
                ),
                sample_size=sample_size,
                warmup=warmup,
                cooldown=cooldown,
                prefer_response_metrics=prefer_response_metrics,
                profile=InfoMixin.extract_from_obj(profile),
                requests=InfoMixin.extract_from_obj(requests),
                backend=InfoMixin.extract_from_obj(backend),
                environment=InfoMixin.extract_from_obj(environment),
            )
            accumulator = accumulator_class(config=config)
            scheduler_state = None
            scheduler: Scheduler[RequestT, ResponseT] = Scheduler()

            async for (
                response,
                request,
                request_info,
                scheduler_state,
            ) in scheduler.run(
                requests=requests,
                backend=backend,
                strategy=strategy,
                env=environment,
                **constraints or {},
            ):
                try:
                    accumulator.update_estimate(
                        response,
                        request,
                        request_info,
                        scheduler_state,
                    )
                    if progress:
                        await progress.on_benchmark_update(
                            accumulator, scheduler_state
                        )
                except Exception as err:  # noqa: BLE001
                    logger.error(
                        "Error updating benchmark estimate/progress: {}", err
                    )

            benchmark = benchmark_class.compile(
                accumulator=accumulator,
                scheduler_state=scheduler_state,  # type: ignore[arg-type]
            )

            if progress:
                await progress.on_benchmark_complete(benchmark)

            yield benchmark

            try:
                strategy, constraints = strategies_generator.send(benchmark)
            except StopIteration:
                strategy = None
                constraints = None

        if progress:
            await progress.on_finalize()