mirror of
https://github.com/esphome/esphome.git
synced 2026-09-02 02:56:01 +00:00
[api] Avoid lambda IIFE and per-byte APIBuffer growth in proxy benchmarks
The InfraredRFReceiveEvent encode benchmark used a C++17 lambda IIFE
(`[]{...}()`) to seed a function-static vector, and the
InfraredRFTransmitRawTimingsRequest decode benchmark grew its APIBuffer
one byte at a time (~210 grow_() calls), each allocating a fresh
exact-fit buffer and memcpy'ing the prior contents. Both patterns are
fine under direct execution but appear to hit a CodSpeed/valgrind
edge case during the simulated benchmark run.
Switch to a plain heap-init pattern for the vector and build the wire
bytes into a stack array first, then resize+memcpy into the APIBuffer
once.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "esphome/components/api/api_pb2.h"
|
||||
#include "esphome/components/api/api_buffer.h"
|
||||
|
||||
@@ -181,19 +183,19 @@ BENCHMARK(Decode_SerialProxyWriteRequest);
|
||||
#if defined(USE_IR_RF) || defined(USE_RADIO_FREQUENCY)
|
||||
|
||||
static APIBuffer build_infrared_rf_transmit_wire() {
|
||||
APIBuffer buf;
|
||||
// Build the entire wire payload into a stack buffer, then copy into the
|
||||
// returned APIBuffer in a single resize+memcpy. Keeps allocation count
|
||||
// low so callgrind/valgrind doesn't churn through hundreds of grow_()s.
|
||||
uint8_t bytes[256];
|
||||
size_t len = 0;
|
||||
|
||||
auto put_byte = [&](uint8_t b) {
|
||||
size_t s = buf.size();
|
||||
buf.resize(s + 1);
|
||||
buf.data()[s] = b;
|
||||
};
|
||||
auto put_byte = [&](uint8_t b) { bytes[len++] = b; };
|
||||
auto put_varint = [&](uint32_t v) {
|
||||
while (v >= 0x80) {
|
||||
put_byte(static_cast<uint8_t>((v & 0x7F) | 0x80));
|
||||
bytes[len++] = static_cast<uint8_t>((v & 0x7F) | 0x80);
|
||||
v >>= 7;
|
||||
}
|
||||
put_byte(static_cast<uint8_t>(v));
|
||||
bytes[len++] = static_cast<uint8_t>(v);
|
||||
};
|
||||
auto encode_zigzag = [](int32_t v) -> uint32_t {
|
||||
return (static_cast<uint32_t>(v) << 1) ^ static_cast<uint32_t>(v >> 31);
|
||||
@@ -212,7 +214,10 @@ static APIBuffer build_infrared_rf_transmit_wire() {
|
||||
put_byte(0x20);
|
||||
put_varint(2);
|
||||
// field 5: timings (packed sint32) — 100 entries alternating mark/space.
|
||||
uint8_t packed[400];
|
||||
// Each entry encodes to 2 bytes (zigzag(560)=1120 → varint 0xE0 0x08), so
|
||||
// packed payload is 200 bytes; with tag (1) + length varint (2) it fits in
|
||||
// the 256-byte stack buffer.
|
||||
uint8_t packed[200];
|
||||
size_t packed_len = 0;
|
||||
for (int i = 0; i < 100; i++) {
|
||||
int32_t value = (i % 2 == 0) ? 560 : -560;
|
||||
@@ -225,9 +230,13 @@ static APIBuffer build_infrared_rf_transmit_wire() {
|
||||
}
|
||||
put_byte(0x2A);
|
||||
put_varint(static_cast<uint32_t>(packed_len));
|
||||
for (size_t i = 0; i < packed_len; i++)
|
||||
put_byte(packed[i]);
|
||||
std::memcpy(bytes + len, packed, packed_len);
|
||||
len += packed_len;
|
||||
// field 6: modulation = 0 — skip (default value, not encoded by senders)
|
||||
|
||||
APIBuffer buf;
|
||||
buf.resize(len);
|
||||
std::memcpy(buf.data(), bytes, len);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
@@ -456,17 +456,17 @@ BENCHMARK(Encode_SerialProxyDataReceived);
|
||||
|
||||
#if defined(USE_IR_RF) || defined(USE_RADIO_FREQUENCY)
|
||||
|
||||
// Mark/space pairs simulating a typical RC-5 / NEC capture (100 timings).
|
||||
static const std::vector<int32_t> &get_ir_timings_100() {
|
||||
static const std::vector<int32_t> timings = [] {
|
||||
std::vector<int32_t> v;
|
||||
v.reserve(100);
|
||||
// Mark/space pairs simulating a typical RC-5 / NEC capture.
|
||||
static std::vector<int32_t> *timings = nullptr;
|
||||
if (timings == nullptr) {
|
||||
timings = new std::vector<int32_t>();
|
||||
timings->reserve(100);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
v.push_back((i % 2 == 0) ? 560 : -560);
|
||||
timings->push_back((i % 2 == 0) ? 560 : -560);
|
||||
}
|
||||
return v;
|
||||
}();
|
||||
return timings;
|
||||
}
|
||||
return *timings;
|
||||
}
|
||||
|
||||
static InfraredRFReceiveEvent make_infrared_rf_receive_event() {
|
||||
|
||||
Reference in New Issue
Block a user