Repository navigation
Conversation
Build the encoded HDS payload in one shared bytearray and pack the tag together with the number, instead of joining many small bytes objects. Output bytes are unchanged. Co-Authored-By: Aiden <aiden@weco.ai>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## dev #552 +/- ##
==========================================
+ Coverage 91.77% 93.17% +1.39%
==========================================
Files 27 30 +3
Lines 4367 5628 +1261
Branches 558 707 +149
==========================================
+ Hits 4008 5244 +1236
- Misses 251 264 +13
- Partials 108 120 +12
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR
pyhap.hds_protocol.encodenow writes every value into one sharedbytearray.bytesobjects, and each container joined them again.dataSenddata frame: 422.74 µs -> 230.70 µs, 45% faster.python3 -m pytest tests/test_hds_recording.py tests/test_hksv_transport.py tests/test_hksv_gating.py -q: 56 passed.What & Why
Message.encode()runs for every HomeKit Data Stream frame the accessory sends. The oldencodereturned a newbytesobject for each tag, number, string, key and container. Each list and dict then joined the parts of its items. A media chunk was copied one time for each container level around it.The new private helper
_encode_into(value, out)appends to onebytearray._encode_intand_encode_length_prefixedappend to the same buffer.struct.packwrites the tag together with the number.encodemakes the buffer, fills it and returnsbytes(out).The tag values, the width limits, the 14-item inline rule and the
TypeErrortext are the same. The public signature and the return type ofencodedo not change.Message.encode()unless noted)control/helloresponse, empty bodydataSend/openresponse, 1 keydataSend/dataevent, 1 packet of 4 KBdataSend/dataevent, 1 packet of 256 KBdataSend/dataevent, 20 packets of 1.5 KBencode(True), one bare scalarencode(data), bare 256 KBbytesThe last two rows get slower. A bare value outside a container now pays for the buffer and the final copy. For a bare 256 KB value, that is two large memory blocks for each call, not one. Every HDS frame is a header dict plus a message dict, so the library does not send bare values.
Why this is safe and easy to maintain
The change is in one file and touches three functions plus one new private helper. The branch order in the encoder is the same as before, so the diff reads line by line. The encoded bytes are identical for every input type. The existing tests in
tests/test_hds_recording.py,tests/test_hksv_transport.pyandtests/test_hksv_gating.pycover the encoder and all pass.Validation
ruff checkandruff format --checkpass onpyhap/hds_protocol.py.Int64, int subclasses, floats,str/bytes/bytearrayat each length limit, 14- and 15-item containers, and the error cases.malloc,calloc,realloc) with the garbage collector off. Each count is for one call of the row, after five warm-up calls.The script below holds the message body of every row. Save it as
bench_rows.pyand runPYTHONPATH=. python3 bench_rows.py data-256kondevand on this branch. Use a new process for each row name.AI assistance
This PR was prepared with AI assistance from Aiden. Candidates were tracked with Weco Observe. The tests, the lint checks and the measurements above ran on the patched tree. There was no human review before submission.
Weco share link: https://dashboard.weco.ai/share/nuxeMBqIu4e-ZZj7maqy9kQufrWB5dHe