8000 Refactor API frame helpers to enable buffer reuse by bdraco · Pull Request #8825 · esphome/esphome · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Refactor API frame helpers to enable buffer reuse #8825

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
May 18, 2025
Merged

Conversation

bdraco
Copy link
Member
@bdraco bdraco commented May 17, 2025

#8805 will heavily conflict. This one has more value so it should merge first.

What does this implement/fix?

This PR refactors the ESPHome API component's frame helper system to enable efficient buffer reuse by passing ProtoWriteBuffer objects instead of raw data pointers. This optimization significantly reduces memory allocations and improves performance for high-frequency message exchanges.

API Frame Helper Interface Updates

  1. Added frame padding and footer size methods:

    • frame_header_padding(): Returns bytes reserved for protocol headers (7 for Noise, 6 for Plaintext)
    • frame_footer_size(): Returns footer size (16 bytes for Noise MAC, 0 for Plaintext)
  2. Renamed and updated write method:

    • write_packet()write_protobuf_packet()
    • Now accepts ProtoWriteBuffer* instead of raw data pointers
    • Enables in-place header writing without data copying

Buffer Management Strategy

The new implementation uses a single reusable buffer with pre-allocated padding:

  1. Pre-allocation: A single buffer is allocated once and reused for multiple messages
  2. Header Padding: Buffer starts with pre-inserted padding bytes (7 for Noise, 6 for Plaintext)
  3. Message Encoding: Protocol buffer data is encoded starting after the padding area
  4. In-place Header Writing: Headers are written into the pre-allocated padding space without moving data
  5. Footer Space: Additional space is reserved for protocol footers (Noise MAC)

Noise performance changes

Before: ~66% performance penalty for encrypted connections --- it was all suffering from memory copy
After: ~7% performance penalty for encrypted connections

base branch add_api_stats
testing branch reuse_buffer_api_stats
Read is also somehow faster without the other upcoming optmizations but its likely because its not churning memory anymore -- avg read_packet: 0.19ms → 0.07ms (63% faster in new version)

Single Loop Period Stats (Last 60s)

  • Old Version: count=6754, avg=0.50ms, max=204ms, total=3407ms
  • New Version: count=6862, avg=0.31ms, max=184ms, total=2121ms
  • Improvement:
    • Average: 38% faster (0.50ms → 0.31ms)
    • Total time: 37% reduction (3407ms → 2121ms)
    • Max time: 10% reduction (204ms → 184ms)

Loop Total Stats (Since Boot)

  • Old Version: count=256929, avg=0.52ms, max=205ms, total=132979ms
  • New Version: count=20568, avg=0.31ms, max=184ms, total=6446ms
  • Improvement:
    • Average: 40% faster (0.52ms → 0.31ms)
    • Max time: 10% reduction (205ms → 184ms)

Plaintext performance changes

As expected its not much difference: write_packet operations (6% faster). The overall loop time decreased by ~3.6%

Seems to help a little bit with free memory as well
Screenshot 2025-05-18 at 12 04 42 AM

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Code quality improvements to existing code or addition of tests
  • Other

Related issue or feature (if applicable):

Pull request in esphome-docs with documentation (if applicable):

  • esphome/esphome-docs#

Testing

external_components:
  - source: github://pr#8825
    components: [ api ]
    refresh: 0s

Test Environment

  • ESP32
  • ESP32 IDF
  • ESP8266
  • RP2040
  • BK72xx
  • RTL87xx

Example entry for config.yaml:

# Example config.yaml

Checklist:

  • The code change is tested and works locally.
  • Tests have been added to verify that the new code works (under tests/ folder).

If user exposed functionality or configuration variables are added/changed:

@bdraco bdraco marked this pull request as ready for review May 17, 2025 21:21
@Copilot Copilot AI review requested due to automatic review settings May 17, 2025 21:21
@bdraco bdraco requested a review from OttoWinter as a code owner May 17, 2025 21:21
@probot-esphome
Copy link

Hey there @OttoWinter, mind taking a look at this pull request as it has been labeled with an integration (api) you are listed as a code owner for? Thanks!
(message by CodeOwnersMention)

Copy link
Contributor
@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the ESPHome API frame helpers to enable efficient buffer reuse by switching from raw data pointers to using ProtoWriteBuffer objects and updating header/footer management accordingly.

  • Refactored write methods to use ProtoWriteBuffer instead of raw pointers.
  • Added methods to provide protocol-specific header padding and footer sizes.
  • Updated documentation and connection buffer management to accommodate the new buffer reuse strategy.

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
esphome/components/api/protocol_details.md Added detailed documentation for both Noise and Plaintext protocol frames.
esphome/components/api/proto.h Introduced a new encode_to_buffer method in ProtoVarInt.
esphome/components/api/api_frame_helper.h Updated APIFrameHelper interface and constructors in derived classes.
esphome/components/api/api_frame_helper.cpp Refactored write methods for Noise and Plaintext to support ProtoWriteBuffer.
esphome/components/api/api_connection.h/cpp Adjusted buffer pre-allocation/insertion and write method calls.
Comments suppressed due to low confidence (1)

esphome/components/api/api_frame_helper.h:96

  • Avoid performing a double move on 'ctx'. Use a single std::move(ctx) to improve code clarity and avoid any unintended side effects.
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx) : socket_(std::move(socket)), ctx_(std::move(std::move(ctx))) {

@bdraco bdraco mentioned this pull request May 18, 2025
14 tasks
@jesserockz jesserockz merged commit b0c1e0e into dev May 18, 2025
29 checks passed
@jesserockz jesserockz deleted the reuse_buffer branch May 18, 2025 21:05
@jesserockz jesserockz added this to the 2025.5.0b3 milestone May 18, 2025
@bdraco
Copy link
Member Author
bdraco commented May 18, 2025

Thanks. I'll fix the conflicts on #8805 after the PyCon dinner

@jesserockz jesserockz mentioned this pull request May 18, 2025
@jesserockz jesserockz mentioned this pull request May 21, 2025
sa-crespo pushed a commit to sa-crespo/esphome that referenced this pull request May 26, 2025
@github-actions github-actions bot locked and limited conversation to collaborators Jun 26, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Different indicator bytes in plaintext and encrypted API. Differing naming schemes. Lacking API documentation.
3 participants
0