Skip to content

[mypyc] Call generator helper method directly in await expression #19376

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 22 commits into from
Jul 7, 2025

Conversation

JukkaL
Copy link
Collaborator

@JukkaL JukkaL commented Jul 4, 2025

Previously calls like await foo() were compiled to code that included code like this (in Python-like pseudocode):

a = foo()
...
b = get_coro(a)
...
c = next(b)

In the above code, get_coro(a) just returns a if foo is a native async function, so we now optimize this call away. Also next(b) calls b.__next__(), which calls the generated generator helper method __mypyc_generator_helper__. Now we call the helper method directly, which saves some unnecessary calls.

More importantly, in a follow-up PR I can easily change the way __mypyc_generator_helper__ is called, since we now call it directly. This makes it possible to avoid raising a StopIteration exception in many await expressions. The goal of this PR is to prepare for the latter optimization. This PR doesn't help performance significantly by itself.

In order to call the helper method directly, I had to generate the declaration of this method and the generated generator class before the main irbuild pass, since otherwise a call site could be processed before we have processed the called generator.

I also improved test coverage of related functionality. We don't have an IR test for async calls, since the IR is very verbose. I manually inspected the generated IR to verify that the new code path works both when calling a top-level function and when calling a method. I'll later add a mypyc benchmark to ensure that we will notice if the performance of async calls is degraded.

# Give a more precise type for generators, so that we can optimize
# code that uses them. They return a generator object, which has a
# specific class. Without this, the type would have to be 'object'.
ret: RType = RInstance(self.fdef_to_generator[fdef])
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is the first part of the optimization.

and val.type.class_ir.has_method(helper_method)
):
# This is a generated generator class, and we can use a fast path.
iter_val: Value = val
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The changes in this function implemented the bulk of the optimization.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe add a bit more detailed comment (essentially summarizing the PR description) so we will not forget the motivation.

Copy link
Member

@ilevkivskyi ilevkivskyi left a comment

Choose a reason for hiding this comment

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

LG, just one suggestion.

and val.type.class_ir.has_method(helper_method)
):
# This is a generated generator class, and we can use a fast path.
iter_val: Value = val
Copy link
Member

Choose a reason for hiding this comment

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

Maybe add a bit more detailed comment (essentially summarizing the PR description) so we will not forget the motivation.

@JukkaL JukkaL merged commit ada0d2a into master Jul 7, 2025
13 checks passed
@JukkaL JukkaL deleted the mypyc-await-optimize-1 branch July 7, 2025 14:43
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.

2 participants