Skip to content

gh-159503: Avoid copying the unsent remainder of a bytes write in asyncio selector transports - #159504

Merged
kumaraditya303 merged 5 commits into
python:mainfrom
Ar-maan05:asyncio-write-remainder-copy
Oct 29, 2026
Merged

kumaraditya303 merged 5 commits into
python:mainfrom
Ar-maan05:asyncio-write-remainder-copy

Conversation

@Ar-maan05

@Ar-maan05 Ar-maan05 commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

When a large bytes write is queued behind pending data, every partial send re-queued the leftover as b[nbytes:], copying the rest of the object each time, so the write cost O(n**2). One write of 48 MiB behind a 2 MiB prefix on a socketpair (Linux, sendmsg path):

before: 1633 ms
after:    14 ms

The fix wraps the remainder in a memoryview in _adjust_leftover_buffer() and _write_send(), the same way write() already does on its first-send path. A bytearray passed to write() now stays exported while its remainder is pending on these paths too, which the first-send path already does today.

Tested by extending the existing partial-write tests to bytes input and checking that the leftover is a view on the original object, test_asyncio passing, and a differential harness that drives the old and new methods with a scripted socket (partial sends, BlockingIOError, send and sendmsg, bytes/bytearray/memoryview input): identical output over 1200 runs.

…in asyncio selector transports

When a large bytes write is queued behind pending data, every partial send re-queued the leftover as b[nbytes:], copying the rest of the object each time. Keep the remainder as a memoryview in _adjust_leftover_buffer() and _write_send(), as write() already does on its first-send path.

@maurycy maurycy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment thread Lib/test/test_asyncio/test_selector_events.py Outdated
Comment thread Misc/NEWS.d/next/Library/2026-10-16-01-56-15.gh-issue-159503.WgYiow.rst Outdated
Address review: test_write_ready_partial and test_write_sendmsg_partial now run for both bytes and memoryview input and check that the remainder is a view on the original object. Wrap the NEWS entry at 78 columns.
Comment thread Lib/asyncio/selector_events.py Outdated
Comment thread Lib/test/test_asyncio/test_selector_events.py Outdated
Comment thread Lib/test/test_asyncio/test_selector_events.py Outdated
Comment thread Misc/NEWS.d/next/Library/2026-10-16-01-56-15.gh-issue-159503.WgYiow.rst Outdated
@Ar-maan05

Copy link
Copy Markdown
Contributor Author

Thanks! Pushed: direct slice for memoryviews, bytearray added to the tests, NEWS trimmed, and a line in the write() docs about not modifying the data until it's sent.

@cmaloney cmaloney left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When making changes look for ways to simplify and what existing docs say. For instance the "must not be mutated" is common in other docs around this same sort of thing.

Comment thread Doc/library/asyncio-protocol.rst Outdated
Comment thread Lib/test/test_asyncio/test_selector_events.py Outdated
Comment thread Lib/test/test_asyncio/test_selector_events.py Outdated
Comment thread Misc/NEWS.d/next/Library/2026-10-16-01-56-15.gh-issue-159503.WgYiow.rst Outdated
@Ar-maan05

Copy link
Copy Markdown
Contributor Author

Thanks both, applied all three!

@read-the-docs-community

read-the-docs-community Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Documentation build overview

📚 cpython-previews | 🛠️ Build #35498753 | 📁 Comparing 792a686 against main (7e5227f)

  🔍 Preview build  

2 files changed
± library/asyncio-protocol.html
± whatsnew/changelog.html

Comment thread Lib/asyncio/selector_events.py Outdated
The send paths are back to plain slicing, since every buffered item is now a memoryview.
@kumaraditya303
kumaraditya303 enabled auto-merge (squash) October 29, 2026 13:49
@kumaraditya303
kumaraditya303 merged commit 43a4511 into python:main Oct 29, 2026
57 checks passed
@Ar-maan05
Ar-maan05 deleted the asyncio-write-remainder-copy branch October 29, 2026 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants