Inline encode_varint_raw fast path and add [[likely]] hints

Apply the same inline fast-path / noinline slow-path split to
ProtoWriteBuffer::encode_varint_raw that was done for ProtoSize::varint.

For values < 128 (field tags, small field values, short lengths), the
single-byte write is now inlined at each call site instead of going
through a full function call. The multi-byte loop is outlined into
encode_varint_raw_slow_().

Also add [[likely]] to both varint fast paths (ProtoSize::varint and
encode_varint_raw) to hint branch prediction.
This commit is contained in:
J. Nick Koston
2026-03-07 20:12:33 -10:00
parent 9c245100ed
commit 507c29add7
2 changed files with 19 additions and 7 deletions
+10
View File
@@ -10,6 +10,16 @@ static const char *const TAG = "api.proto";
uint32_t ProtoSize::varint_slow(uint32_t value) { return varint_wide(value); }
void ProtoWriteBuffer::encode_varint_raw_slow_(uint32_t value) {
do {
this->debug_check_bounds_(1);
*this->pos_++ = static_cast<uint8_t>(value | 0x80);
value >>= 7;
} while (value > 0x7F);
this->debug_check_bounds_(1);
*this->pos_++ = static_cast<uint8_t>(value);
}
#ifdef USE_API_VARINT64
optional<ProtoVarInt> ProtoVarInt::parse_wide(const uint8_t *buffer, uint32_t len, uint32_t *consumed,
uint32_t result32) {
+9 -7
View File
@@ -240,14 +240,13 @@ class ProtoWriteBuffer {
ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer), pos_(buffer->data() + buffer->size()) {}
ProtoWriteBuffer(std::vector<uint8_t> *buffer, size_t write_pos)
: buffer_(buffer), pos_(buffer->data() + write_pos) {}
void encode_varint_raw(uint32_t value) {
while (value > 0x7F) {
inline void ESPHOME_ALWAYS_INLINE encode_varint_raw(uint32_t value) {
if (value < 128) [[likely]] {
this->debug_check_bounds_(1);
*this->pos_++ = static_cast<uint8_t>(value | 0x80);
value >>= 7;
*this->pos_++ = static_cast<uint8_t>(value);
return;
}
this->debug_check_bounds_(1);
*this->pos_++ = static_cast<uint8_t>(value);
this->encode_varint_raw_slow_(value);
}
void encode_varint_raw_64(uint64_t value) {
while (value > 0x7F) {
@@ -378,6 +377,9 @@ class ProtoWriteBuffer {
std::vector<uint8_t> *get_buffer() const { return buffer_; }
protected:
// Slow path for encode_varint_raw values >= 128, outlined to keep fast path small
void encode_varint_raw_slow_(uint32_t value) __attribute__((noinline));
#ifdef ESPHOME_DEBUG_API
void debug_check_bounds_(size_t bytes, const char *caller = __builtin_FUNCTION());
void debug_check_encode_size_(uint32_t field_id, uint32_t expected, ptrdiff_t actual);
@@ -512,7 +514,7 @@ class ProtoSize {
* @return The number of bytes needed to encode the value
*/
static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE varint(uint32_t value) {
if (value < 128)
if (value < 128) [[likely]]
return 1; // Fast path: 7 bits, most common case
if (__builtin_is_constant_evaluated())
return varint_wide(value);