mirror of
https://github.com/esphome/esphome.git
synced 2026-09-11 15:27:33 +00:00
[api] Keep the decode loop register resident and inline the varint fast path
CodSpeed showed the single virtual costing 7 to 18 percent on the decode benchmarks. The x86-64 disassembly pointed at the call, not the switch: passing the field number and wire type alongside the tag plus a 16 byte union payload kept five values live across the call, so the compiler spilled this, the end pointer and half of the payload to the stack and reloaded them for every field. decode_field() now takes only the tag, the payload pointer (already the loop cursor) and one scalar that holds the varint or fixed32 value or the payload length. The generated override wraps them in a ProtoFieldValue that never exists in memory. On the host the switch key is the field number derived with one shift and the guard compares the whole tag against the constant the case declares, which is the same two instructions the old per wire type dispatch cost. The loop also handles single byte varints inline instead of going through the parse result struct, which drops the materialized consumed count and its add on every tag and small value.
This commit is contained in:
@@ -237,7 +237,7 @@ class TypeInfo(ABC):
|
||||
"""Emit one decode_field() case for a field and the wire type it expects."""
|
||||
return (
|
||||
f"case PROTO_DECODE_CASE({self.number}, {int(wire_type)}):\n"
|
||||
f" PROTO_DECODE_GUARD(wire_type, {int(wire_type)});\n"
|
||||
f" PROTO_DECODE_GUARD(tag, {self.number}, {int(wire_type)});\n"
|
||||
f" {body}\n"
|
||||
f" break;"
|
||||
)
|
||||
@@ -2741,15 +2741,16 @@ def build_message_type(
|
||||
if decode:
|
||||
# One virtual per message: the shared decode loop parses the payload for the wire
|
||||
# type and hands it over with the tag, so a single switch covers every field.
|
||||
o = f"bool {desc.name}::decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) {{\n"
|
||||
o += " switch (PROTO_DECODE_KEY(tag, field_id)) {\n"
|
||||
o = f"bool {desc.name}::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) {{\n"
|
||||
o += " const ProtoFieldValue value(data, scalar);\n"
|
||||
o += " switch (PROTO_DECODE_KEY(tag)) {\n"
|
||||
o += indent("\n".join(decode), " ") + "\n"
|
||||
o += " default: return false;\n"
|
||||
o += " }\n"
|
||||
o += " return true;\n"
|
||||
o += "}\n"
|
||||
cpp += o
|
||||
prot = "bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;"
|
||||
prot = "bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;"
|
||||
protected_content.insert(0, prot)
|
||||
|
||||
# Generate custom decode() override for messages with FixedVector fields
|
||||
|
||||
Reference in New Issue
Block a user