mirror of
https://github.com/esphome/esphome.git
synced 2026-09-16 09:38:42 +00:00
Merge branch 'dev' into binary-sensor-remove-optional-state
This commit is contained in:
@@ -166,7 +166,13 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
||||
item->component = component;
|
||||
item->set_name(name_type, static_name, hash_or_id);
|
||||
item->type = type;
|
||||
item->callback = std::move(func);
|
||||
// Use destroy + placement-new instead of move-assignment.
|
||||
// GCC's std::function::operator=(function&&) does a full swap dance even when the
|
||||
// target is empty. Since recycled/new items always have an empty callback, we can
|
||||
// destroy the empty one (no-op) and move-construct directly, saving ~40 bytes of
|
||||
// swap/destructor code on Xtensa.
|
||||
item->callback.~function();
|
||||
new (&item->callback) std::function<void()>(std::move(func));
|
||||
// Reset remove flag - recycled items may have been cancelled (remove=true) in previous use
|
||||
this->set_item_removed_(item, false);
|
||||
item->is_retry = is_retry;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
from tests.testing_helpers import ComponentManifestOverride
|
||||
|
||||
|
||||
def override_manifest(manifest: ComponentManifestOverride) -> None:
|
||||
manifest.enable_codegen()
|
||||
@@ -0,0 +1,61 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#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
|
||||
@@ -0,0 +1 @@
|
||||
binary_sensor:
|
||||
@@ -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
|
||||
@@ -0,0 +1,78 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#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
|
||||
@@ -0,0 +1 @@
|
||||
sensor:
|
||||
Reference in New Issue
Block a user