Files
esphome/tests/benchmarks/components/api/bench_proto_decode.cpp
T
J. Nick Koston 38ff332fec [core] Add CodSpeed C++ benchmarks for protobuf and main loop
Add automated benchmarks using Google Benchmark to prevent performance
regressions in the API protobuf encoding/decoding and core loop paths.

Benchmarks cover:
- Protobuf encode: SensorState, BinarySensorState, HelloResponse,
  LightState, DeviceInfoResponse (20 nested devices + 20 areas)
- Protobuf decode: HelloRequest, SwitchCommand, LightCommand
- Protobuf calculate_size and full calc+encode send path
- Varint parse/encode/size for various value ranges
- Scheduler call/next_schedule_in with idle and active timers
- Application loop component dispatch and blocking guard overhead

Infrastructure:
- Extract shared build logic from cpp_unit_test.py into test_helpers.py
- Add cpp_benchmark.py mirroring the unit test build pattern
- Support benchmark.yaml per component dir for declaring dependencies
- Add CodSpeed CI workflow triggered on api/core changes
- Fix ProtoMessage protected destructor on host platform
2026-03-16 19:25:46 -10:00

120 lines
3.0 KiB
C++

#include <benchmark/benchmark.h>
#include "esphome/components/api/api_pb2.h"
#include "esphome/components/api/api_buffer.h"
namespace esphome::api::benchmarks {
// --- HelloRequest decode (string + varint fields) ---
static void BM_Decode_HelloRequest(benchmark::State &state) {
// Manually encoded HelloRequest:
// field 1 (string): "aioesphomeapi"
// field 2 (varint): 1 (api_version_major)
// field 3 (varint): 10 (api_version_minor)
uint8_t encoded[] = {
0x0A, 0x0D, // field 1, length 13
'a', 'i', 'o', 'e', 's', 'p', 'h', 'o', 'm', 'e', 'a', 'p', 'i', // "aioesphomeapi"
0x10, 0x01, // field 2, value 1
0x18, 0x0A, // field 3, value 10
};
for (auto _ : state) {
HelloRequest msg;
msg.decode(encoded, sizeof(encoded));
benchmark::DoNotOptimize(msg.api_version_major);
}
}
BENCHMARK(BM_Decode_HelloRequest);
// --- SwitchCommandRequest decode (simple command) ---
static void BM_Decode_SwitchCommandRequest(benchmark::State &state) {
// field 1 (fixed32): key = 0x12345678
// field 2 (varint): state = true
uint8_t encoded[] = {
0x0D, 0x78, 0x56, 0x34, 0x12, // field 1, fixed32
0x10, 0x01, // field 2, varint true
};
for (auto _ : state) {
SwitchCommandRequest msg;
msg.decode(encoded, sizeof(encoded));
benchmark::DoNotOptimize(msg.state);
}
}
BENCHMARK(BM_Decode_SwitchCommandRequest);
// --- LightCommandRequest decode (complex command with many fields) ---
static void BM_Decode_LightCommandRequest(benchmark::State &state) {
uint8_t encoded[] = {
// field 1: key (fixed32) = 0x11223344
0x0D,
0x44,
0x33,
0x22,
0x11,
// field 2: has_state (varint) = true
0x10,
0x01,
// field 3: state (varint) = true
0x18,
0x01,
// field 4: has_brightness (varint) = true
0x20,
0x01,
// field 5: brightness (fixed32/float) = 0.8
0x2D,
0xCD,
0xCC,
0x4C,
0x3F,
// field 9: has_rgb (varint) = true
0x48,
0x01,
// field 10: red (fixed32/float) = 1.0
0x55,
0x00,
0x00,
0x80,
0x3F,
// field 11: green (fixed32/float) = 0.5
0x5D,
0x00,
0x00,
0x00,
0x3F,
// field 12: blue (fixed32/float) = 0.2
0x65,
0xCD,
0xCC,
0x4C,
0x3E,
// field 20: has_effect (varint) = true
0xA0,
0x01,
0x01,
// field 21: effect (string) = "rainbow"
0xAA,
0x01,
0x07,
'r',
'a',
'i',
'n',
'b',
'o',
'w',
};
for (auto _ : state) {
LightCommandRequest msg;
msg.decode(encoded, sizeof(encoded));
benchmark::DoNotOptimize(msg.brightness);
}
}
BENCHMARK(BM_Decode_LightCommandRequest);
} // namespace esphome::api::benchmarks