Repository navigation
[mypyc] Add am_send slot and use PyIter_Send for await/yield from - #21979
Merged
Merged
Conversation
Generated generator and coroutine classes now install an am_send slot that adapts __mypyc_generator_helper__ to CPython's C send protocol. Yielded values use PYGEN_NEXT, normal returns use PYGEN_RETURN and an out parameter, and errors use PYGEN_ERROR. StopIteration escaping the helper also maps to PYGEN_RETURN, preserving the known mypyc bug where an explicit StopIteration in a generator body is treated as normal completion. CPyIter_Send now wraps PyIter_Send and returns normal completion through an out parameter. The generic await/yield from path uses it for the initial advance and ordinary resumed sends, avoiding StopIteration overhead. Statically known native children retain the direct generator-helper fast path. asyncio's C Task also reaches generated coroutines through the new am_send slot. Across 8 interleaved benchmark rounds on CPython 3.14, median gains are 25-26% for dynamically typed immediate awaits, 33-37% for dynamically typed suspended awaits, 20% for one statically typed suspended await, and 26-30% for two. Native coroutine chains of length 5 and 20 improve by about 10% and 3-5%. Immediate static awaits are unchanged, and uvloop results are inconclusive. Since the baseline already optimizes statically known resumed children, chain20 improves from 1.75x to 1.83x interpreted speed with the GIL and from 1.63x to 1.67x with free threading.
p-sawicki
approved these changes
Sep 14, 2026
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.
This speeds up async defs and generators when crossing dynamic/static boundaries.
Generated generator and coroutine classes now install an
am_sendslot thatadapts
__mypyc_generator_helper__to CPython's C send protocol. Yielded valuesuse PYGEN_NEXT, normal returns use PYGEN_RETURN and an out parameter, and errors
use PYGEN_ERROR.
CPyIter_Sendnow wrapsPyIter_Sendand returns normal completion through an outparameter. The generic await/yield from path uses it for the initial advance
and ordinary resumed sends, avoiding StopIteration overhead. Statically known
native children retain the direct generator-helper fast path. asyncio's C Task
also reaches generated coroutines through the new am_send slot.
In a microbenchmark on CPython 3.14, median gains are 25-26% for dynamically
typed immediate awaits, 33-37% for dynamically typed suspended awaits,
20% for one statically typed suspended await, and 26-30% for two.
In realistic asyncio workloads gains will be much smaller.
I used heavy coding agent assist, but iterated on results quite a lot (mostly tests and
comments).