-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Improve middleware performance #9200
Conversation
The contextmanager in cff60eb added quite a bit of overhead and was only used for fixing the current app when using middleware. I did a github code search and did not find usage of set_current_app outside of aiohttp so I think its safe to remove as its unlikely to be used outside of aiohttp.
Codecov ReportAll modified and coverable lines are covered by tests ✅
✅ All tests successful. No failed tests found. Additional details and impacted files@@ Coverage Diff @@
## master #9200 +/- ##
=======================================
Coverage 98.31% 98.31%
=======================================
Files 107 107
Lines 34483 34507 +24
Branches 4093 4100 +7
=======================================
+ Hits 33901 33927 +26
+ Misses 411 410 -1
+ Partials 171 170 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
Is it possibly caused by the exception handling or something? |
Yeah it looks like the |
I don't think enter is an issue. I believe But, exit handles an exception in the expected case. I can't think of any way to make that more efficient though, other than not using the decorator... |
The profile shows the most expensive part is Line 108 on cpython |
I recall we had a similar problem with rendering templates in Home Assistant (we can render many per second) and the solution was to create the context manager once and use it over and over... that worked because there was no await and everything finished synchronously, but we can't use that solution here. |
Looks to me like there's a ContextDecorator which uses _recreate_cm() to avoid redoing the init everytime. It seems to me like the decorators could be improved a little to do that same thing instead of instantiating a new class directly on every call. Not sure how much you'd get from that, but atleast would save fetching the docs attribute etc. in the init. |
Looks like thats already supposed to be happening but https://github.com/python/cpython/blob/fcfe78664bf740a7fb059f9817e584a612e09f54/Lib/contextlib.py#L128 |
I played around with it for a bit and came to the conclusion that trying to make the context manager performant is likely not possible without writing it all out which got large quickly. I think what we have now is probably the simplest solution. |
Maybe I'm misreading that, but doesn't the decorator transform the original function into a new function which returns a new _GeneratorContextManager on every call? Seems to me like the _GeneratorContextManager would need to be created in the outer function and then called in the inner function in order for _recreate_cm() to ever be called.. |
The decorator would run on the function (unbound method), right? So, the decorator is not being run every time. |
Decorator runs once, but context manager is created every time. Example:
|
Well, yes, that's my point above.. Seems to me like the function should be written something like:
At which point, each call would only be running:
|
Hacky proof-of-concept:
Note that I get 1 ms tottime in init on the original, and nothing with 1ms tottime on this version. |
Switching between contextmanager and contextmanager2 and timing with:
I get around 1.4 - 2.3 µs with the original and 0.9 - 1.3 µs with the hack. So, original is about 50-80% slower. Though just setting self.gen is probably not correct (it won't be thread-safe for example), so would need to make some change for that.. |
Taking the instantiation out:
I get as low as 0.8µs with the hack and no lower than 1.7 µs with the original, so actually over 100% slower. |
Hmm, I actually can't see how that So, something like this should be safe and I can still get <1 µs from it.
|
Backport to 3.10: 💔 cherry-picking failed — conflicts found❌ Failed to cleanly apply 42930b0 on top of patchback/backports/3.10/42930b0c023af52782ec026d0e850f5f0c0bcdcd/pr-9200 Backporting merged PR #9200 into master
🤖 @patchback |
Backport to 3.11: 💔 cherry-picking failed — conflicts found❌ Failed to cleanly apply 42930b0 on top of patchback/backports/3.11/42930b0c023af52782ec026d0e850f5f0c0bcdcd/pr-9200 Backporting merged PR #9200 into master
🤖 @patchback |
(cherry picked from commit 42930b0)
(cherry picked from commit 42930b0)
… forward websocket subprotocols from the request headers https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst#3106-2024-09-24
What do these changes do?
The contextmanager in cff60eb added quite a bit of overhead and was only used for fixing the current app when using middleware. I did a github code search and did not find usage of
set_current_app
outside of aiohttp so I think its safe to remove as its unlikely to be used outside of aiohttp.closes #9196
Are there changes in behavior for the user?
The
set_current_app
function has been removed.Is it a substantial burden for the maintainers to support this?
no