diff --git a/tests/benchmarks/components/binary_sensor/__init__.py b/tests/benchmarks/components/binary_sensor/__init__.py new file mode 100644 index 00000000000..b08f67a0956 --- /dev/null +++ b/tests/benchmarks/components/binary_sensor/__init__.py @@ -0,0 +1,5 @@ +from tests.testing_helpers import ComponentManifestOverride + + +def override_manifest(manifest: ComponentManifestOverride) -> None: + manifest.enable_codegen() diff --git a/tests/benchmarks/components/binary_sensor/bench_binary_sensor_publish.cpp b/tests/benchmarks/components/binary_sensor/bench_binary_sensor_publish.cpp new file mode 100644 index 00000000000..8bae943e2ea --- /dev/null +++ b/tests/benchmarks/components/binary_sensor/bench_binary_sensor_publish.cpp @@ -0,0 +1,61 @@ +#include + +#include "esphome/components/binary_sensor/binary_sensor.h" + +namespace esphome::binary_sensor::benchmarks { + +static constexpr int kInnerIterations = 2000; + +// Benchmark: publish_state with alternating values (forces state change every time) +static void BinarySensorPublish_Alternating(benchmark::State &state) { + BinarySensor sensor; + + // First publish to establish initial state + sensor.publish_initial_state(false); + + for (auto _ : state) { + for (int i = 0; i < kInnerIterations; i++) { + sensor.publish_state(i % 2 == 0); + } + benchmark::DoNotOptimize(sensor.state); + } + state.SetItemsProcessed(state.iterations() * kInnerIterations); +} +BENCHMARK(BinarySensorPublish_Alternating); + +// Benchmark: publish_state with same value (tests dedup fast path) +static void BinarySensorPublish_NoChange(benchmark::State &state) { + BinarySensor sensor; + + sensor.publish_initial_state(true); + + for (auto _ : state) { + for (int i = 0; i < kInnerIterations; i++) { + sensor.publish_state(true); + } + benchmark::DoNotOptimize(sensor.state); + } + state.SetItemsProcessed(state.iterations() * kInnerIterations); +} +BENCHMARK(BinarySensorPublish_NoChange); + +// Benchmark: publish_state with a callback registered +static void BinarySensorPublish_WithCallback(benchmark::State &state) { + BinarySensor sensor; + + int callback_count = 0; + sensor.add_on_state_callback([&callback_count](bool) { callback_count++; }); + + sensor.publish_initial_state(false); + + for (auto _ : state) { + for (int i = 0; i < kInnerIterations; i++) { + sensor.publish_state(i % 2 == 0); + } + benchmark::DoNotOptimize(callback_count); + } + state.SetItemsProcessed(state.iterations() * kInnerIterations); +} +BENCHMARK(BinarySensorPublish_WithCallback); + +} // namespace esphome::binary_sensor::benchmarks diff --git a/tests/benchmarks/components/binary_sensor/benchmark.yaml b/tests/benchmarks/components/binary_sensor/benchmark.yaml new file mode 100644 index 00000000000..fc0db6c52c9 --- /dev/null +++ b/tests/benchmarks/components/binary_sensor/benchmark.yaml @@ -0,0 +1 @@ +binary_sensor: diff --git a/tests/benchmarks/components/sensor/__init__.py b/tests/benchmarks/components/sensor/__init__.py new file mode 100644 index 00000000000..5a593aa8c27 --- /dev/null +++ b/tests/benchmarks/components/sensor/__init__.py @@ -0,0 +1,12 @@ +import esphome.codegen as cg +from tests.testing_helpers import ComponentManifestOverride + + +def override_manifest(manifest: ComponentManifestOverride) -> None: + # Sensor filter benchmarks need USE_SENSOR_FILTER defined. + # We use a custom to_code instead of enable_codegen() to avoid + # pulling in the full sensor component setup. + async def to_code(config): + cg.add_define("USE_SENSOR_FILTER") + + manifest.to_code = to_code diff --git a/tests/benchmarks/components/sensor/bench_sensor_filter.cpp b/tests/benchmarks/components/sensor/bench_sensor_filter.cpp new file mode 100644 index 00000000000..e4aa3976901 --- /dev/null +++ b/tests/benchmarks/components/sensor/bench_sensor_filter.cpp @@ -0,0 +1,78 @@ +#include + +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/sensor/filter.h" + +namespace esphome::sensor::benchmarks { + +static constexpr int kInnerIterations = 2000; + +// Benchmark: sensor publish through a SlidingWindowMovingAverageFilter (window=5, send_every=1) +static void SensorFilter_SlidingWindowAvg(benchmark::State &state) { + Sensor sensor; + + // Create filter: window_size=5, send_every=1, send_first_at=1 + auto *filter = new SlidingWindowMovingAverageFilter(5, 1, 1); + sensor.add_filter(filter); + + float value = 0.0f; + for (auto _ : state) { + for (int i = 0; i < kInnerIterations; i++) { + sensor.publish_state(value); + value += 0.1f; + if (value > 1000.0f) + value = 0.0f; + } + benchmark::DoNotOptimize(sensor.state); + } + state.SetItemsProcessed(state.iterations() * kInnerIterations); +} +BENCHMARK(SensorFilter_SlidingWindowAvg); + +// Benchmark: sensor publish through ExponentialMovingAverageFilter +static void SensorFilter_ExponentialMovingAvg(benchmark::State &state) { + Sensor sensor; + + // alpha=0.1, send_every=1, send_first_at=1 + auto *filter = new ExponentialMovingAverageFilter(0.1f, 1, 1); + sensor.add_filter(filter); + + float value = 0.0f; + for (auto _ : state) { + for (int i = 0; i < kInnerIterations; i++) { + sensor.publish_state(value); + value += 0.1f; + if (value > 1000.0f) + value = 0.0f; + } + benchmark::DoNotOptimize(sensor.state); + } + state.SetItemsProcessed(state.iterations() * kInnerIterations); +} +BENCHMARK(SensorFilter_ExponentialMovingAvg); + +// Benchmark: sensor publish through a chain of 3 filters (offset + multiply + sliding window) +static void SensorFilter_Chain3(benchmark::State &state) { + Sensor sensor; + + sensor.add_filters({ + new OffsetFilter(1.0f), + new MultiplyFilter(2.0f), + new SlidingWindowMovingAverageFilter(5, 1, 1), + }); + + float value = 0.0f; + for (auto _ : state) { + for (int i = 0; i < kInnerIterations; i++) { + sensor.publish_state(value); + value += 0.1f; + if (value > 1000.0f) + value = 0.0f; + } + benchmark::DoNotOptimize(sensor.state); + } + state.SetItemsProcessed(state.iterations() * kInnerIterations); +} +BENCHMARK(SensorFilter_Chain3); + +} // namespace esphome::sensor::benchmarks diff --git a/tests/benchmarks/components/sensor/benchmark.yaml b/tests/benchmarks/components/sensor/benchmark.yaml new file mode 100644 index 00000000000..e1fb52cdd6a --- /dev/null +++ b/tests/benchmarks/components/sensor/benchmark.yaml @@ -0,0 +1 @@ +sensor: