Files
cleveragents-core/benchmarks/execution_throughput_bench.py
HAL9000 ea4998ba61
CI / status-check (pull_request) Blocked by required conditions
CI / benchmark-publish (pull_request) Has been skipped
CI / push-validation (pull_request) Successful in 25s
CI / helm (pull_request) Successful in 33s
CI / build (pull_request) Successful in 54s
CI / lint (pull_request) Successful in 1m17s
CI / typecheck (pull_request) Successful in 1m28s
CI / quality (pull_request) Successful in 1m33s
CI / security (pull_request) Successful in 1m39s
CI / integration_tests (pull_request) Successful in 3m46s
CI / e2e_tests (pull_request) Successful in 4m2s
CI / unit_tests (pull_request) Successful in 4m42s
CI / docker (pull_request) Successful in 1m49s
CI / coverage (pull_request) Successful in 11m51s
CI / benchmark_regression (pull_request) Failing after 28m22s
CI / benchmark-regression (pull_request) Failing after 28m30s
perf(ci): optimize benchmark-regression test suite to reduce CI execution time
Added benchmark_regression_fast nox session that excludes the three slowest benchmark suites (IndexingScalingSuite, ContextAssemblyScalingSuite, ExecutionThroughputSuite) from PR regression checks. These suites have timeouts of 300-600 s each and were the primary contributors to the 50+ minute CI execution time.

Added benchmark_regression CI job to ci.yml using the fast session with a 20-minute timeout. Added full benchmark_regression run to the nightly quality workflow so the complete suite still runs on a schedule.

Documented the excluded suites and their timeout characteristics in each benchmark file for future maintainers.

ISSUES CLOSED: #1668
2026-04-23 17:49:42 +00:00

129 lines
4.2 KiB
Python

"""ASV benchmarks for plan execution throughput at scale.
Measures sequential and concurrent plan execution overhead at varying
plan counts (10, 50, 100). Uses the lightweight in-process executor
path (no database, no LLM) to isolate execution-dispatch cost.
"""
from __future__ import annotations
import importlib
import sys
from pathlib import Path
from typing import ClassVar
from unittest.mock import MagicMock
# Ensure the local *source* tree is importable even when ASV has an
# older build of the package installed.
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
import cleveragents # noqa: E402
importlib.reload(cleveragents)
from ulid import ULID # noqa: E402
from cleveragents.application.services.plan_execution_context import ( # noqa: E402
PlanExecutionContext,
RuntimeExecuteActor,
)
from cleveragents.application.services.plan_executor import ( # noqa: E402
PlanExecutor,
StrategyDecision,
)
from cleveragents.domain.models.core.change import ( # noqa: E402
InMemoryChangeSetStore,
)
from cleveragents.tool.registry import ToolRegistry # noqa: E402
from cleveragents.tool.runner import ToolRunner # noqa: E402
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
_RESOURCE_ID = "01HGZ6FE0AQDYTR4BXVQZ6EB00"
def _make_runner() -> ToolRunner:
return ToolRunner(registry=ToolRegistry())
def _make_decisions(count: int) -> list[StrategyDecision]:
"""Build a linear chain of *count* decisions."""
root_id = str(ULID())
return [
StrategyDecision(
decision_id=root_id if i == 0 else str(ULID()),
step_text=f"Step {i + 1}",
sequence=i,
parent_id=root_id if i > 0 else None,
)
for i in range(count)
]
def _execute_single_plan(runner: ToolRunner) -> None:
"""Execute one plan with 3 decisions (fire-and-forget)."""
plan_id = str(ULID())
ctx = PlanExecutionContext(
plan_id=plan_id,
changeset_store=InMemoryChangeSetStore(),
)
actor = RuntimeExecuteActor(tool_runner=runner, execution_context=ctx)
actor.execute(decisions=_make_decisions(3))
# ---------------------------------------------------------------------------
# Parameterized execution throughput suite
# ---------------------------------------------------------------------------
class ExecutionThroughputSuite:
"""Benchmark plan execution throughput at varying plan counts.
Full parameter set: [10, 50, 100] (used in nightly runs).
This suite is excluded from the fast PR subset via ``benchmark_regression_fast``
because the 50 and 100 plan cases require up to 300 s each. See issue #1668.
"""
params: ClassVar[list[int]] = [10, 50, 100]
param_names: ClassVar[list[str]] = ["plan_count"]
timeout = 300
_runner: ToolRunner
def setup(self, plan_count: int) -> None:
"""Prepare a shared tool runner."""
self._runner = _make_runner()
def time_sequential_plans(self, plan_count: int) -> None:
"""Execute *plan_count* plans sequentially."""
for _ in range(plan_count):
_execute_single_plan(self._runner)
def time_executor_construction(self, plan_count: int) -> None:
"""Construct *plan_count* PlanExecutor instances."""
lifecycle = MagicMock()
for _ in range(plan_count):
ctx = PlanExecutionContext(
plan_id=str(ULID()),
changeset_store=InMemoryChangeSetStore(),
)
PlanExecutor(
lifecycle_service=lifecycle,
tool_runner=self._runner,
execution_context=ctx,
)
def time_decision_tree_scaling(self, plan_count: int) -> None:
"""Execute one plan with *plan_count* decisions."""
plan_id = str(ULID())
ctx = PlanExecutionContext(
plan_id=plan_id,
changeset_store=InMemoryChangeSetStore(),
)
actor = RuntimeExecuteActor(tool_runner=self._runner, execution_context=ctx)
actor.execute(decisions=_make_decisions(plan_count))