Skip to content
/ django Public

Comments

Optimized MiddlewareMixin coroutine check.#15216

Merged
felixxm merged 1 commit intodjango:mainfrom
adamchainz:optimize_middleware_mixin
Dec 21, 2021
Merged

Optimized MiddlewareMixin coroutine check.#15216
felixxm merged 1 commit intodjango:mainfrom
adamchainz:optimize_middleware_mixin

Conversation

@adamchainz
Copy link
Member

Cache get_response’s coroutine status. Afaiu get_response is never expected to change.

Repeating the check on every is unnecessary work - about 1 microsecond each time, on my machine's Python 3.10:

In [2]: async def foo(): return

In [3]: def bar(): return

In [4]: %timeit asyncio.iscoroutinefunction(foo)
979 ns ± 16.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [5]: %timeit asyncio.iscoroutinefunction(bar)
1.04 µs ± 10.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

@kezabelle
Copy link
Contributor

kezabelle commented Dec 20, 2021

Hey Adam,
I'd spotted this a while back too, but I didn't know enough about how the machinery of async and ASGI works to say for certain that the wrapping up of callables into a get_response would always be static after init. So I'm glad you're pushing it, where I was too uncertain :)

When I was looking at it, I was thinking about re-using the _is_coroutine attribute which is (unfortunately) conditionally set, and doing getattr(self, '_is_coroutine', False); presumably it's slightly faster the way you've done it by adding another attribute?

Copy link
Member

@carltongibson carltongibson left a comment

Choose a reason for hiding this comment

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

Afaiu get_response is never expected to change.

This is where we find folks dynamically munging middleware functions. 😜 (I agree that's not expected, so the change should be OK.)

For reference:

(I don't really have strong feelings here… — happy to go with what you agree.)

@adamchainz adamchainz force-pushed the optimize_middleware_mixin branch from 9987a06 to 842665a Compare December 20, 2021 17:23
@adamchainz
Copy link
Member Author

adamchainz commented Dec 20, 2021

I'd spotted this a while back too, but I didn't know enough about how the machinery of async and ASGI works to say for certain that the wrapping up of callables into a get_response would always be static after init. So I'm glad you're pushing it, where I was too uncertain :)

Great minds think alike!

When I was looking at it, I was thinking about re-using the _is_coroutine attribute which is (unfortunately) conditionally set, and doing getattr(self, '_is_coroutine', False); presumably it's slightly faster the way you've done it by adding another attribute?

Absolutely right, I missed that, and have updated to use it.

We can see asyncio.iscoroutine checks the attribute to be its special object, so None seems like a legit alternative: https://github.com/python/cpython/blob/e9898bf153d26059261ffef11f7643ae991e2a4c/Lib/asyncio/coroutines.py#L21-L24

This is where we find folks dynamically munging middleware functions. 😜 (I agree that's not expected, so the change should be OK.)

This would already be broken, as async_check() is not rerun

@adamchainz adamchainz closed this Dec 20, 2021
@adamchainz adamchainz reopened this Dec 20, 2021
@adamchainz adamchainz force-pushed the optimize_middleware_mixin branch from 842665a to fc82b30 Compare December 20, 2021 17:26
@felixxm felixxm merged commit 33401cb into django:main Dec 21, 2021
@adamchainz adamchainz deleted the optimize_middleware_mixin branch December 21, 2021 09:05
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