mirror of
https://github.com/esphome/esphome.git
synced 2026-07-10 17:05:36 +00:00
[api] Inline ProtoSize::varint fast path for hot loop performance
Split ProtoSize::varint() into an always-inlined fast path (value < 128) and a noinline slow path, eliminating indirect function calls on the BLE advertisement encode hot path. Also force-inline calc_uint32() and calc_length() so the compiler fully inlines size calculations per advertisement instead of emitting out-of-line calls. Verified via Xtensa disassembly: eliminates ~80 indirect function calls per 16-advertisement batch flush (3 calc + varint per advertisement). Total hot path code reduced from 24 to 21 functions.
This commit is contained in:
@@ -8,6 +8,19 @@ namespace esphome::api {
|
||||
|
||||
static const char *const TAG = "api.proto";
|
||||
|
||||
uint32_t ProtoSize::varint_slow_(uint32_t value) {
|
||||
// value is guaranteed >= 128 here (fast path handled inline)
|
||||
if (value < 16384) {
|
||||
return 2; // 14 bits
|
||||
} else if (value < 2097152) {
|
||||
return 3; // 21 bits
|
||||
} else if (value < 268435456) {
|
||||
return 4; // 28 bits
|
||||
} else {
|
||||
return 5; // 32 bits (maximum for uint32_t)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_API_VARINT64
|
||||
optional<ProtoVarInt> ProtoVarInt::parse_wide(const uint8_t *buffer, uint32_t len, uint32_t *consumed,
|
||||
uint32_t result32) {
|
||||
|
||||
@@ -511,23 +511,23 @@ class ProtoSize {
|
||||
* @param value The uint32_t value to calculate size for
|
||||
* @return The number of bytes needed to encode the value
|
||||
*/
|
||||
static constexpr uint32_t varint(uint32_t value) {
|
||||
// Optimized varint size calculation using leading zeros
|
||||
// Each 7 bits requires one byte in the varint encoding
|
||||
static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE varint(uint32_t value) {
|
||||
if (value < 128)
|
||||
return 1; // 7 bits, common case for small values
|
||||
|
||||
// For larger values, count bytes needed based on the position of the highest bit set
|
||||
if (value < 16384) {
|
||||
return 2; // 14 bits
|
||||
} else if (value < 2097152) {
|
||||
return 3; // 21 bits
|
||||
} else if (value < 268435456) {
|
||||
return 4; // 28 bits
|
||||
} else {
|
||||
return 5; // 32 bits (maximum for uint32_t)
|
||||
return 1; // Fast path: 7 bits, most common case
|
||||
if (__builtin_is_constant_evaluated()) {
|
||||
// Compile-time: full cascade for constexpr callers
|
||||
if (value < 16384)
|
||||
return 2;
|
||||
if (value < 2097152)
|
||||
return 3;
|
||||
if (value < 268435456)
|
||||
return 4;
|
||||
return 5;
|
||||
}
|
||||
return varint_slow_(value);
|
||||
}
|
||||
// Slow path for varint >= 128, outlined to keep fast path small
|
||||
static uint32_t varint_slow_(uint32_t value) __attribute__((noinline));
|
||||
|
||||
/**
|
||||
* @brief Calculates the size in bytes needed to encode a uint64_t value as a varint
|
||||
@@ -609,7 +609,7 @@ class ProtoSize {
|
||||
static constexpr uint32_t calc_int32_force(uint32_t field_id_size, int32_t value) {
|
||||
return field_id_size + (value < 0 ? 10 : varint(static_cast<uint32_t>(value)));
|
||||
}
|
||||
static constexpr uint32_t calc_uint32(uint32_t field_id_size, uint32_t value) {
|
||||
static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_uint32(uint32_t field_id_size, uint32_t value) {
|
||||
return value ? field_id_size + varint(value) : 0;
|
||||
}
|
||||
static constexpr uint32_t calc_uint32_force(uint32_t field_id_size, uint32_t value) {
|
||||
@@ -644,7 +644,7 @@ class ProtoSize {
|
||||
static constexpr uint32_t calc_uint64_force(uint32_t field_id_size, uint64_t value) {
|
||||
return field_id_size + varint(value);
|
||||
}
|
||||
static constexpr uint32_t calc_length(uint32_t field_id_size, size_t len) {
|
||||
static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_length(uint32_t field_id_size, size_t len) {
|
||||
return len ? field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len) : 0;
|
||||
}
|
||||
static constexpr uint32_t calc_length_force(uint32_t field_id_size, size_t len) {
|
||||
|
||||
Reference in New Issue
Block a user