perf(subscriber): take arrived rows without a task per row - #101
Merged
Merged
Conversation
recv_many learned whether a row had arrived by starting a read and giving the loop a turn, once per row: 10.6-13.9 us of client CPU a row, against recv's ~2.5, so batches() cost more than recv. It now looks at websockets' queue of received frames, which a read pops without suspending, and costs 1.3 us a row against recv's 1.45. A catch-up's rows still go through the read-ahead. The queue is private websockets API: websockets is capped below 18, and test_batches checks its shape against the installed release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FsSDkeb5rVAxA1FSmKKfQi
Merged
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.
recv_manylearned whether a row had arrived by starting a read (ensure_future) and giving the loop a turn (sleep(0)), once per row. That madebatches()cost more per row thanrecv, although its docstring says it costs less.Now, for rows from the socket, it checks
websockets' queue of received frames,recv_messages.frames.queue. When a whole message is at the head of that queue, the read pops it without suspending, so no task or loop turn is needed. When it isn't, the batch ends and nothing is read ahead. Rows from a catch-up still use the read-ahead, since cancelling the generator would end it.Client CPU per row, 100k frames already queued:
recvrecv_many(500)Private API.
websocketsis capped at>=14,<18. The queue has the same shape in 14 through 17.1.test_the_queue_it_looks_at_is_websocketsruns against a real server and fails if a release moves the queue, so raising the cap only needs that test to pass.Tests (each fails against the old loop or without the
fincheck):test_a_batch_from_the_socket_starts_no_tasktest_from_the_socket_nothing_is_read_ahead, which replaces the read-ahead testtest_a_fragmented_message_ends_the_batch_and_loses_nothingtest_the_queue_it_looks_at_is_websocketsDocs:
recv_manydocstring and API.md were already correct once this lands.The OTel example still uses
recv. Receiving now costs about 1.4 µs a row against about 8 µs to convert and queue each span, and OTel's processors take one record per call, sobatches()wouldn't change much there.🤖 Generated with Claude Code
https://claude.ai/code/session_01FsSDkeb5rVAxA1FSmKKfQi