You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
contextlib.contextmanager is quite popular, and using it currently involves executing slow Python method calls in with statements. We could specialize this in mypyc for decent performance gains.
I also experimented with manually rewriting some context managers in mypy to not rely on contextlib.contextmanager, but this was actually quite tedious and makes code harder to maintain, so it's not a great alternative.
Since general context managers are pretty hard to optimize, we could instead add librt.contextlib.contextmanager that only works for sufficiently simple context managers.
First, it should be easy to support context managers with a single yield statement at the top level block (generate a class with suitable __enter__ and __exit__ methods):
Or maybe I'm overthinking this -- maybe it's sufficient to compile a normal generator and call the generator methods directly when the context manager is used.
@JukkaL Yes, I think simply avoiding a bunch of slow Python calls should be enough (although I didn't do any profiling I intuitively feel this is where the problem occurs). I don't think we will need to look into the generator body, just make sure all calls (i.e. with ... -> .send() etc) are fast C-level calls.
I took a shot at it, but (assuming I did it correctly) it's slower than the original...
TBH I am surprised. We have special handling for StopIteration (normally we don't raise it and instead return a special value, stop_iter_ptr). And any other exceptions should happen very rarely. I guess it would make sense to look into what you did in details.
Activity
ilevkivskyi commentedon Jan 22, 2026
Now that mypy is faster this can bring potentially big perf win for mypy, so I raised priority to high.
JukkaL commentedon Jan 22, 2026
Since general context managers are pretty hard to optimize, we could instead add
librt.contextlib.contextmanagerthat only works for sufficiently simple context managers.First, it should be easy to support context managers with a single
yieldstatement at the top level block (generate a class with suitable__enter__and__exit__methods):Second,
yieldintry/finallyis also common:Again, we should be able to compile this into a class with
__enter__and__exit__.The third most common use case seems to be
yieldwithin a with statement. This might not be too hard either, but I'm not sure.JukkaL commentedon Jan 22, 2026
Or maybe I'm overthinking this -- maybe it's sufficient to compile a normal generator and call the generator methods directly when the context manager is used.
ilevkivskyi commentedon Jan 22, 2026
@JukkaL Yes, I think simply avoiding a bunch of slow Python calls should be enough (although I didn't do any profiling I intuitively feel this is where the problem occurs). I don't think we will need to look into the generator body, just make sure all calls (i.e.
with ...->.send()etc) are fast C-level calls.ilevkivskyi commentedon Jan 22, 2026
That said, if we are going this way, it looks like we will need to replicate all the tedious dances with exceptions in
__exit__()from here https://github.com/python/cpython/blob/main/Lib/contextlib.py#L145hauntsaninja commentedon Jan 27, 2026
I took a shot at it, but (assuming I did it correctly) it's slower than the original... I think exception handling is slow, looks like #476 is related
ilevkivskyi commentedon Jan 29, 2026
TBH I am surprised. We have special handling for
StopIteration(normally we don't raise it and instead return a special value,stop_iter_ptr). And any other exceptions should happen very rarely. I guess it would make sense to look into what you did in details.