Commit Graph
13792 Commits
Author SHA1 Message Date
J. Nick Koston 91dbc0f341 Merge branch 'codspeed-benchmarks' into scheduler-process-to-add-fast-path 2026-03-17 08:45:47 -10:00
J. Nick Koston f57c9d0501 Merge branch 'dev' into codspeed-benchmarks 2026-03-17 08:45:33 -10:00
J. Nick Koston 81788951da Exclude tests/benchmarks/ from clang-tidy file discovery 2026-03-17 08:44:56 -10:00
J. Nick Koston 82ccc37ba1 [ethernet] Mark EthernetComponent as final (#14842) 2026-03-17 08:14:52 -10:00
J. Nick Koston 3826e95506 [api] Fix ProtoMessage protected destructor compile error on host platform (#14882) 2026-03-17 08:14:36 -10:00
Jonathan Swoboda b083491e74 [microphone] Switch IDF test to new I2S driver (#14886) 2026-03-17 13:46:32 -04:00
Diorcet Yann 73ca0ff106 [core] Small improvements (#14884) 2026-03-17 09:22:31 -04:00
J. Nick Koston be9d8b463e Merge branch 'codspeed-benchmarks' into scheduler-process-to-add-fast-path 2026-03-17 02:11:40 -10:00
J. Nick Koston 9c9464f29f Skip to_add_count_ field on single-threaded platforms
On ESPHOME_THREAD_SINGLE there are no concurrent writers, so
to_add_empty_() checks to_add_.empty() directly. The counter
field and its increment/clear operations are compiled out.
2026-03-17 02:09:39 -10:00
J. Nick Koston 4003406f8b Remove Scheduler_NextScheduleIn benchmark — too cheap, flaps 2026-03-17 02:06:16 -10:00
J. Nick Koston 1ed30414c9 Fix duplicate comment line in to_remove_ documentation 2026-03-17 02:05:07 -10:00
J. Nick KostonandCopilot Autofix powered by AI a8bb3d8c6f Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-03-17 02:02:23 -10:00
pre-commit-ci-lite[bot] d0c39d95ad [pre-commit.ci lite] apply automatic fixes 2026-03-17 12:02:06 +00:00
J. Nick KostonandCopilot Autofix powered by AI 1c685a8a77 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-03-17 02:00:37 -10:00
J. Nick KostonandCopilot Autofix powered by AI f2ab81a0ef Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-03-17 02:00:17 -10:00
J. Nick Koston 55a76fcbca Change cleanup_() to return bool instead of size_t
cleanup_() was computing items_.size() on every call even on the
fast path where nothing was removed. All callers only check if
items remain (== 0), so return bool and use items_.empty() instead.
2026-03-17 01:58:25 -10:00
J. Nick Koston f654f70c39 Merge branch 'codspeed-benchmarks' into scheduler-process-to-add-fast-path 2026-03-17 01:53:03 -10:00
J. Nick Koston a1009f7a5c Use relaxed memory ordering for all counter atomics
The mutex already provides all necessary memory ordering for the
counter operations. acquire/release fences on the fast-path reads
were causing unnecessary cache flushes, regressing
Scheduler_NextScheduleIn by ~10%.
2026-03-17 01:51:46 -10:00
J. Nick Koston e14443b9c3 Fix ProtoSize_Varint benchmarks: prevent constant folding
The compiler constant-folds ProtoSize::varint(42) to 1 and optimizes
the entire inner loop to a single addition, causing ~62ns jitter-dominated
measurements. Use varying inputs (i & 0x7F for small, 0xFFFF0000 | i for
large) so each call computes a real result.
2026-03-17 01:50:36 -10:00
J. Nick Koston 9d9ac41028 Merge branch 'codspeed-benchmarks' into scheduler-process-to-add-fast-path 2026-03-17 01:45:03 -10:00
J. Nick Koston 587179143f Rename BENCHMARK_BINARY to BUILD_BINARY for generic use 2026-03-17 01:43:58 -10:00
J. Nick Koston b87b4102e0 Fix binary path extraction and defer benchmark compile error
- Use BENCHMARK_BINARY= marker for reliable binary path extraction
  instead of fragile tail -1 (PlatformIO can print warnings after path)
- Fix Scheduler_Defer: defer() is protected on Component, use
  set_timeout(delay=0) directly on Scheduler instead
2026-03-17 01:42:37 -10:00
J. Nick Koston 602d6af350 Merge branch 'codspeed-benchmarks' into scheduler-process-to-add-fast-path 2026-03-17 01:38:58 -10:00
J. Nick Koston d5f2c93e68 Add benchmarks for set_timeout, set_interval, and defer registration
Measures the cost of registering scheduler items:
- Scheduler_SetTimeout: set_timeout with 1s delay (heap path)
- Scheduler_SetInterval: set_interval with 1s period (heap path + offset calc)
- Scheduler_Defer: Component::defer (set_timeout with delay=0)

Uses i%5 for IDs to exercise the cancel-existing-timer path that
happens when re-registering with the same ID.
2026-03-17 01:38:19 -10:00
J. Nick Koston ca73a0c2c9 Merge branch 'codspeed-benchmarks' into scheduler-process-to-add-fast-path 2026-03-17 01:38:00 -10:00
J. Nick Koston a577715d5a [scheduler] Skip lock in process_to_add/cleanup/defer when nothing to do
Add fast-path checks in process_to_add(), cleanup_(), and
process_defer_queue_() to skip mutex acquisition when there is
nothing to process.

Uses std::atomic<uint32_t> counters on platforms with atomics support
(ESP32, host), falls back to always taking the lock on platforms
without atomics (LibreTiny). Single-threaded platforms (ESP8266,
RP2040, nRF52) check directly since there are no concurrent writers.

Also migrates existing to_remove_ to the same atomic pattern and
moves the defer_queue_.size() snapshot under the lock — both were
previously plain reads without the lock while being modified
cross-thread.
2026-03-17 01:36:35 -10:00
J. Nick Koston 804e2330ec Ifdef out WarnIfComponentBlockingGuard for benchmark builds
Add USE_BENCHMARK define to benchmark build flags. Guard the
warn_blocking call in finish() with #ifndef USE_BENCHMARK so
scheduler benchmarks using fake monotonic time don't trigger
the underflow (fake now > real millis()).

Remove BenchComponent — no longer needed with the ifdef.
2026-03-17 01:16:34 -10:00
J. Nick Koston e01670eed8 Revert core changes, use intervals with fake time for scheduler benchmark
The warn_blocking underflow only happens with fake time in benchmarks,
not in production (millis() is monotonic). Accept the consistent
overhead from one warning per call — CodSpeed regression detection
works on relative changes, not absolute values.
2026-03-17 01:14:39 -10:00
J. Nick Koston 2785edaac8 [core] Clamp underflowed blocking_time in warn_blocking cold path
When millis() < started_ (e.g. scheduler passes a now value slightly
ahead of real millis()), the uint32_t subtraction in finish() wraps to
~4 billion. This caused warn_blocking to fire on every call since the
underflowed value always exceeds the uint16_t threshold max (65535).

Fix by clamping blocking_time to uint16_t max in the cold warn_blocking
path. After one warning, should_warn_of_blocking() saturates the
threshold to 65535 and subsequent clamped values (65535) don't exceed it.
Zero cost on the hot path — the clamp is in the noinline cold function.
2026-03-17 01:11:48 -10:00
J. Nick Koston 885d7c3938 [core] Fix uint32_t underflow in WarnIfComponentBlockingGuard::finish()
When curr_time (from millis()) is less than started_ (the `now` passed
to scheduler.call()), the subtraction wraps to a huge value (~4 billion).
This triggers spurious blocking warnings with nonsensical times.

This can happen when the scheduler's execute_item_() returns a millis()
value that subsequent items use as their guard start, but the next
scheduler.call() passes a `now` value from a slightly different source.

Fix by skipping the blocking check when curr_time < started_ (underflow).

Also restore the scheduler firing benchmark to use intervals with
monotonically increasing fake time, now that the guard handles underflow.
2026-03-17 01:10:57 -10:00
J. Nick Koston 1f9380ddc0 Fix scheduler firing benchmark: no inner loop, warm-up call
- Revert component.h change (no core changes for benchmarks)
- Remove -DWARN_IF_BLOCKING_OVER_MS from build flags (can't shadow constexpr)
- Drop inner loop — 5 heap pops + callbacks + pushes per call is well
  above CodSpeed's 60ns instrumentation overhead
- Add warm-up call before benchmark loop to trigger the blocking guard
  once and ramp the threshold
- interval=0 causes infinite loop, must use interval=1 with fake time
2026-03-17 01:05:04 -10:00
J. Nick Koston ec60e2c228 Fix scheduler benchmark: use fake time with guard disabled at compile time
interval=0 causes infinite loop (reschedules at same time, never breaks).
interval=1 with millis() doesn't work (real time doesn't advance fast
enough between inner iterations for intervals to re-fire).

Solution: use interval=1 with monotonically increasing fake time (now++)
and disable WarnIfComponentBlockingGuard at compile time via
-DWARN_IF_BLOCKING_OVER_MS=UINT32_MAX in benchmark build flags. This
prevents the guard's (millis() - started_) underflow when fake time
exceeds real millis().
2026-03-17 01:00:06 -10:00
J. Nick Koston d7be19703b Fix scheduler intervals not firing and warn_blocking
Use interval=0 so all 5 intervals fire unconditionally every call().
Pass real millis() to scheduler.call() so WarnIfComponentBlockingGuard
doesn't see fake time ahead of wall clock (which causes uint32_t
underflow in the blocking time calculation).
2026-03-17 00:54:29 -10:00
J. Nick Koston 4565167bec Fix scheduler benchmark triggering warn_blocking
WarnIfComponentBlockingGuard compares the `now` passed to
scheduler.call() against real millis() in finish(). Using fake time
ahead of real millis() caused uint32_t underflow in the guard, triggering
blocking warnings. Fix by reading real millis() at the start of each
outer iteration so fake time stays close to wall clock.
2026-03-17 00:53:10 -10:00
J. Nick Koston 662780bcc6 Simplify scheduler now comment 2026-03-17 00:38:48 -10:00
J. Nick Koston e45cfc5451 Revert scheduler now reset — must be monotonic for millis_64_from_
millis_64_from_() tracks 32-bit rollovers, so going backwards in time
would appear as a ~49 day forward jump. Keep now monotonically
increasing across all iterations. With 2000 inner iterations per outer
iteration, overflow is not a practical concern.
2026-03-17 00:37:58 -10:00
J. Nick Koston ca2cf4044c Fix stray quote in main.cpp and scheduler time overflow
- Remove trailing " from AUTO GENERATED INCLUDE BLOCK END comment
- Reset scheduler `now` at start of each outer iteration to avoid
  unbounded growth toward UINT32_MAX across benchmark iterations
2026-03-17 00:35:53 -10:00
J. Nick Koston eb94465174 Add logger benchmarks for format string hot paths
Three benchmarks covering the most common ESP_LOGW patterns:
- Logger_NoFormat: plain string, no format specifiers (fastest path)
- Logger_3Uint32: 3x uint32_t (common for status/diagnostics)
- Logger_3Float: 3x float with precision (common for sensor values)
2026-03-17 00:23:15 -10:00
J. Nick Koston 8c058cd725 Replace hand-encoded protobuf bytes with programmatic encoding in decode benchmarks
Encode messages once in setup using the real protobuf API, then decode
the resulting bytes in the benchmark loop. This keeps decode benchmarks
automatically in sync with the protobuf schema — hand-encoded byte
arrays would silently break when fields change.
2026-03-17 00:20:52 -10:00
J. Nick Koston 3fdb201f59 Suppress blocking warnings in scheduler benchmarks
Under valgrind, 2000 inner iterations take long enough in wall clock
to trigger WarnIfComponentBlockingGuard. Use a BenchComponent subclass
that sets warn_if_blocking_over_ to UINT16_MAX to prevent log noise.
2026-03-17 00:16:44 -10:00
J. Nick Koston ed539e17ff Add scheduler benchmark with 5 intervals firing per call
Adds Scheduler_Call_5IntervalsFiring: 5 intervals with 1ms period,
time advancing each inner iteration so all 5 fire every call().
This benchmarks the real scheduler hot path where callbacks execute.
2026-03-16 23:57:39 -10:00
J. Nick Koston 2061fa2393 Add inner loops to _Fresh benchmarks for CodSpeed consistency
Both _Fresh variants now use kInnerIterations with fresh buffer
creation inside the inner loop. This amortizes CodSpeed's per-iteration
instrumentation overhead while still measuring alloc+calc+encode per item.
2026-03-16 23:53:19 -10:00
J. Nick Koston 5d5a48c369 Fix _Fresh benchmark consistency and address review nits
- Remove inner loop from CalcAndEncode_DeviceInfoResponse_Fresh to match
  SensorStateResponse_Fresh — both now measure single alloc+encode per
  iteration as intended for heap allocation cost benchmarking
- Add comments documenting why _Fresh variants skip inner loops
- Add first-wins comment to load_component_yaml_configs
- Clean up .gitignore template comment
2026-03-16 23:52:27 -10:00
J. Nick Koston 8e4091baa3 Increase inner iterations from 1000 to 2000 to reduce jitter 2026-03-16 23:40:18 -10:00
J. Nick Koston dd52c91290 Add inner iteration loops to amortize CodSpeed instrumentation overhead
Sub-microsecond benchmarks are dominated by the ~60ns per-iteration
valgrind start/stop cost in CodSpeed simulation mode. Add kInnerIterations
(1000) inner loops to all fast benchmarks so the actual work dominates.
Move DoNotOptimize calls outside inner loops to prevent artificial overhead.

Also address review feedback:
- Use tokenless CodSpeed (public repo, no CODSPEED_TOKEN needed)
- Fix warning message to show component-specific path
- Fix stray ". :" in error message
- Verify pinned SHA on re-runs to prevent stale checkouts
2026-03-16 23:32:49 -10:00
J. Nick Koston 5b073ca520 Fix stale comment in main.cpp, simplify CI binary extraction 2026-03-16 22:48:39 -10:00
J. Nick Koston e34855b92e Add _Fresh variants to measure heap allocation cost in encode benchmarks 2026-03-16 22:44:16 -10:00
J. Nick Koston 621ba4d5b4 Set loop_interval to 0 in ApplicationLoop benchmark to skip sleep 2026-03-16 22:34:41 -10:00
J. Nick Koston 68cec9cc28 Add real Application::loop() benchmark, call original_setup() in main
- Call original_setup() in benchmark main.cpp so code-generated App
  initialization (pre_setup, area/device registration, looping_components_
  init) runs before benchmarks
- Restore ApplicationLoop_Empty benchmark that calls the actual App.loop()
2026-03-16 22:32:36 -10:00
J. Nick Koston a92147e1c9 Remove BM_ prefix, drop application loop benchmark, fix core includes
- Remove BM_ prefix from all benchmark names (unnecessary convention)
- Remove bench_application_loop.cpp (App needs pre_setup/setup which
  requires full code generation; will revisit as integration benchmark)
- Fix extra_include_dirs to use os.path.relpath for sibling directories
2026-03-16 22:27:03 -10:00