Skip to content

Add CodSpeed performance benchmarks for core hot paths - #175143

Merged
edenhaus merged 14 commits into
devfrom
frenck/add-codspeed-benchmarks
Sep 24, 2026
Merged

edenhaus merged 14 commits into
devfrom
frenck/add-codspeed-benchmarks

Conversation

@frenck

@frenck frenck commented Jun 29, 2026 •

Copy link
Copy Markdown
Member

Proposed change

Adds CodSpeed performance benchmarking to track regressions on the core runtime hot paths, the busiest call sites in the whole process.

This first cut covers the core runtime:

  • Event bus dispatch, state-change tracking, the dispatcher, and timer scheduling.
  • The state machine: create, update, the unchanged-value report fast path, single reads, and full reads at several sizes.
  • The entity write path.
  • The template engine: compile, warm render, render-to-info, and a render that walks the state machine at several sizes.

The benchmarks live in a top-level benchmarks/ directory, outside testpaths, so the regular test suite never collects them. CodSpeed runs them with pytest benchmarks --codspeed.

A few notes on the approach:

  • Size-sensitive paths are parametrized at several scales (10/100/1000), so an algorithmic regression shows up as the curve bending instead of hiding behind a single number.
  • Destructive paths use benchmark.pedantic with setup/teardown so each measured call stays on the branch its name claims (for example, create stays create instead of silently becoming update).
  • Every benchmark asserts the work actually happened, so a benchmark cannot look faster by quietly skipping the work.

Benchmarks run as a job in the existing CI workflow (ci.yaml), reusing the same change detection and the prepared virtual environment as the test jobs, so no extra dependency install is needed. The job uses the CodSpeed v4 action in simulation mode (instrumented, machine-independent measurement) and authenticates through GitHub OIDC, so no CODSPEED_TOKEN secret is needed. It is gated through the existing .core_files.yaml filters, so it only runs when core code or the benchmark suite itself changed, and is skipped on forks where OIDC is unavailable.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue:
  • Link to documentation pull request:
  • Link to developer documentation pull request:
  • Link to frontend pull request:

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies a diff between library versions and ideally a link to the changelog/release notes is added to the PR description.

To help with the load of incoming pull requests:

@frenck
frenck requested a review from a team as a code owner June 29, 2026 20:14
Copilot AI balanced review requested due to automatic review settings June 29, 2026 20:14
@home-assistant home-assistant Bot added cla-signed code-quality small-pr PRs with less than 30 lines. labels Jun 29, 2026
@frenck frenck added the core label Jun 29, 2026
@frenck
frenck marked this pull request as draft June 29, 2026 20:20
@github-actions

Copy link
Copy Markdown

Check requirements

Checked at commit 30b6730.

⚠️ Some checks require attention — see the details below.

Package Old New No Advisories Not Yanked Repo Public CI Upload Release Pipeline Security PR Link Async Safe
pytest-codspeed — 5.0.3 ✅ ✅ ✅ ⚠️ ✅ ☑️ ❌ ✅
📦 pytest-codspeed: 5.0.3
  • No Advisories: ✅ No active advisories reported by PyPI for version 5.0.3.
  • Not Yanked: ✅ Version 5.0.3 is a live (non-yanked) release.
  • Repo Public: ✅ https://github.com/CodSpeedHQ/pytest-codspeed is publicly accessible.
  • CI Upload: ⚠️ No PEP 740 provenance attestation present on PyPI. Upload method cannot be verified from PyPI alone.
  • Release Pipeline: ✅ OIDC trusted publishing via uv publish --trusted-publishing=always on tag-push trigger; publish is gated on event_name == 'push', so workflow_dispatch cannot trigger a release.
  • Security: ☑️ Baseline scan found nothing obvious in __init__.py, plugin.py, config.py, utils.py, instruments/__init__.py, instruments/analysis.py, instruments/walltime.py, pyproject.toml. This is not a security review — only the cheap checks were run.
  • PR Link: ❌ PR description must link to the source repository at https://github.com/CodSpeedHQ/pytest-codspeed. A PyPI page link is not sufficient.
  • Async Safe: ✅ Sync-only library; Home Assistant integrations must wrap calls in an executor.

Generated by Check requirements (AW) · 139.7 AIC · ⌖ 30.1 AIC · ⊞ 29.9K · ◷

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a CodSpeed performance benchmarking harness to guard Home Assistant Core's busiest runtime hot paths (event bus, state machine, state-change tracking, dispatcher, timers, entity writes, and the template engine) against regressions. Benchmarks live in a new top-level benchmarks/ directory that sits outside testpaths, so the normal test suite never collects them; CodSpeed runs them via a dedicated CI workflow gated on core changes.

Changes:

  • Adds a benchmarks/ package with a shared async hass fixture and three benchmark modules covering events, state machine/entity writes, and templates, using benchmark.pedantic (with teardown) and parametrized sizes for size-sensitive paths.
  • Adds the pytest-codspeed==5.0.3 test dependency and a Ruff TID251 per-file ignore so benchmarks may import tests.common.
  • Adds a .github/workflows/codspeed.yml workflow using the CodSpeed v4 action in simulation mode with OIDC auth, gated through .core_files.yaml.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
benchmarks/__init__.py Marks benchmarks/ as a package.
benchmarks/conftest.py Provides the async hass fixture and a populate_states helper for scaling benchmarks.
benchmarks/test_core_events.py Benchmarks event fire/listen, state-change tracking, dispatcher, and timer scheduling.
benchmarks/test_core_states.py Benchmarks state create/update/report/read paths and the entity write path.
benchmarks/test_core_template.py Benchmarks template compile, render, render-to-info, and state-walking renders.
pyproject.toml Adds TID251 ignore for benchmarks/** so test helpers can be imported.
requirements_test.txt Adds the pytest-codspeed==5.0.3 dependency.
.github/workflows/codspeed.yml New CI workflow running benchmarks via CodSpeed, gated on core changes.

Comment thread .github/workflows/codspeed.yml Outdated
Comment thread .github/workflows/codspeed.yml Outdated
Copilot AI review requested due to automatic review settings June 29, 2026 20:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment thread benchmarks/test_core_events.py Outdated
@codspeed

codspeed Bot commented Jun 29, 2026 •

Copy link
Copy Markdown

Congrats! CodSpeed is installed 🎉

🆕 56 new benchmarks were detected.

You will start to see performance impacts in the reports once the benchmarks are run from your default branch.

Detected benchmarks


ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.


Open in CodSpeed

Copilot AI review requested due to automatic review settings June 29, 2026 21:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment thread pyproject.toml
@frenck
frenck marked this pull request as ready for review June 30, 2026 12:24
Comment thread .github/workflows/ci.yaml Outdated
Comment thread .github/workflows/ci.yaml Outdated
Comment thread benchmarks/test_core_events.py Outdated
Comment thread benchmarks/test_core_events.py
Comment thread benchmarks/test_core_events.py
Comment thread benchmarks/test_core_events.py
Comment thread benchmarks/test_core_states.py
…eivers bug

- Remove redundant explanatory comment for CodSpeed mode input
- Add memory mode alongside simulation for allocation measurements
- Fix async_dispatcher_connect test to use distinct receiver callables
  per connection, since receivers are keyed by the callable object
- Assert listener/receiver call counts and filtered/cancelled callbacks
  are (not) invoked, using invariants that hold regardless of how many
  times the benchmark harness calls the measured function
- Assert exact state value in test_state_get instead of just not-None

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 9, 2026 18:18
@frenck
frenck requested a review from edenhaus July 9, 2026 18:19
Copilot AI review requested due to automatic review settings July 20, 2026 12:22

@edenhaus edenhaus left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI failing. Please have a look

Maybe because you specified both modes in one job.

@home-assistant
home-assistant Bot marked this pull request as draft July 20, 2026 12:29
@home-assistant

Copy link
Copy Markdown
Contributor

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread benchmarks/conftest.py
Comment thread pyproject.toml
frenck added 2 commits August 30, 2026 18:59
The venv the base job builds with uv points its shebang at uv's managed
interpreter, which actions/setup-python never installs, so on a cache hit
valgrind found a dangling interpreter and refused to run anything. Use
the action every other venv job here uses.

Drop the rich override with it. uv pip install ignores it, and that is
what builds the venv, so it was doing nothing while reaching every
resolution in the repo.
Copilot AI review requested due to automatic review settings August 30, 2026 19:01
@frenck

frenck commented Aug 30, 2026

Copy link
Copy Markdown
Member Author

@edenhaus Not the two modes, it was the interpreter. Fixed, and dev merged in.

valgrind: /home/runner/work/core/core/venv/bin/pytest: bad interpreter: No such file or directory
failed to execute the benchmark process, exit code: 126

The job set Python up with actions/setup-python and then restored the venv the base job builds with uv venv. That venv's shebang points at uv's managed interpreter, which actions/setup-python never installs, so on a cache hit the whole venv is unrunnable. Copilot called this one on .github/workflows/ci.yaml.

It now uses ./.github/actions/restore-or-build-venv like every other venv job here, which also drops the fail-on-cache-miss brittleness since that action rebuilds on a miss.

I also dropped the [tool.uv] override-dependencies entry for rich, which Copilot flagged three times. uv pip install ignores it, and that is what builds the venv, so it was doing nothing useful while reaching every resolution in the repo. Verifiable in a normal checkout: rich resolves to 10.16.2 (surepy's pin wins) with pytest-codspeed 5.0.3 installed alongside it, and the suite runs.

pytest benchmarks --codspeed --no-cov -o addopts="" is green locally, 27 benchmarks.

../Frenck

                       

Blogging my personal ramblings at frenck.dev

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (2)

benchmarks/conftest.py:25

  • Stop Home Assistant during fixture teardown. async_test_home_assistant does not stop the instance itself (the standard fixture does so explicitly), so every benchmark leaves its instance and resources alive and contaminates later timing and memory measurements.
    async with async_test_home_assistant() as hass:
        yield hass

benchmarks/test_core_states.py:43

  • Prepopulate light.benchmark before invoking the benchmark. The fixture starts with an empty state machine, so the target's first invocation takes the create branch; pytest-codspeed's unmeasured warm-up varies by mode/runtime, making this supposedly update-only measurement mode-dependent.
    counter = 0

uv pip install does honour it, and without it the venv build has no
solution: surepy pins rich below 11, pytest-codspeed needs 13.8.1 up.
Copilot AI review requested due to automatic review settings August 30, 2026 19:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (3)

Previously missed (1) — in code that hasn't changed since the last review.

.core_files.yaml:16

  • Handle benchmark-only changes explicitly in the info job. test_full_suite defaults to true and is only changed for integration updates (ci.yaml:173,186-215), so leaving this filter out of any does not prevent a benchmark-only PR from scheduling the full test suite as the comment claims.
# Performance benchmark suite (CodSpeed). Only gates the benchmark job; kept out
# of the `any` aggregate below so it does not pull in the full test suite.
benchmarks: &benchmarks

benchmarks/conftest.py:25

  • Stop each benchmark Home Assistant instance during fixture teardown. async_test_home_assistant itself only restores test state (tests/common.py:365-372); the normal hass fixture separately calls async_stop (tests/conftest.py:720-741). Without that step, every benchmark leaves a running instance in INSTANCES, retaining its event loop and resources for the rest of the simulation/memory run.
    async with async_test_home_assistant() as hass:
        yield hass

benchmarks/test_core_events.py:178

  • Give this delayed callback the argument required by async_call_later. The helper passes the UTC fire time to its action (homeassistant/helpers/event.py:1552-1560), so this zero-argument listener would raise TypeError if the timer ran and could make the cancellation check pass without incrementing called.
    @callback
    def listener() -> None:
        nonlocal called
        called += 1

@frenck
frenck marked this pull request as ready for review August 30, 2026 19:27
@home-assistant
home-assistant Bot requested a review from edenhaus August 30, 2026 19:27
Copilot AI review requested due to automatic review settings September 11, 2026 15:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The workflow check currently fails, and the benchmark fixture does not configure Home Assistant’s event loop consistently with production.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 9/9 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread .github/workflows/ci.yaml Outdated
Comment thread benchmarks/conftest.py
Comment on lines +8 to +24
from collections.abc import AsyncGenerator, Callable

import pytest

from homeassistant.core import HomeAssistant
from tests.common import async_test_home_assistant


@pytest.fixture
async def hass() -> AsyncGenerator[HomeAssistant]:
"""Return a running Home Assistant instance for benchmarking.

Most hot paths under test (``async_fire``, ``async_set``, ``async_render``)
are ``@callback`` methods, so the benchmark fixture can drive them
synchronously from within the running loop.
"""
async with async_test_home_assistant() as hass:
Comment thread requirements_test.txt
pipdeptree==4.2.2
pytest-asyncio==1.4.0
pytest-aiohttp==1.1.1
pytest-codspeed==5.0.3
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 24, 2026 14:23

@edenhaus edenhaus left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @frenck 👍

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Multiple moderate benchmark correctness, teardown, and CI filtering issues remain unresolved.

Review effort: Balanced
Findings: 1 Medium severity · 1 Low severity

Open (2)
Resolved since last review (1)

@edenhaus
edenhaus merged commit 8b593a8 into dev Sep 24, 2026
53 checks passed
@edenhaus
edenhaus deleted the frenck/add-codspeed-benchmarks branch September 24, 2026 14:41
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 25, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants