Speed up Local - #551
Conversation
47fd682 to
ece894a
Compare
|
I have seen Local show up in profiles as well, and I don't even use async, so it'd be great to have its performance improved. I wonder given that |
Sure. Triplicating the logic into the functions would probably make a lot of sense for performance, but those functions should probably be comment-marked "there be dragons here, be very careful" 😄 |
|
@bluetech Great news: according to my measurements it is indeed a whole lot faster, another +100% on top of the context-manager version 😄 |
|
Gentle review nudge, @carltongibson? 😄 (Ditto for #552) |
|
@akx Nice 😃 — I'm just working my way back to asgiref now, as it happens. 🎁 |
carltongibson
left a comment
There was a problem hiding this comment.
@akx I'm going to defer this one for now. I've been focused on a whole load of deadlock issues, and want to get the fixes for those out before taking on thinking about this properly.
13dc288 to
2bed574
Compare
|
@carltongibson Okay 👍 I rebased this in any case. OPS gains remain approximately the same as before: |
|
Super. The rebase is helpful |
|
Rebased. Still benchmarks pleasantly faster against 3.12.1 (a7cc976) 😄 by min timeby average ops |
|
Hey @akx. Right, I finally got to sit down with this with a clear head. So, yes. OK I see you. My concern is maintenance, especially with the repetitive code. So, comments and, even better, tests. How do we stop people (me) editing this wrong in the future, and how do we tell if they (I) have? |
5e0ac94 to
00688fe
Compare
There were, IMO, fairly copious comments (most of the critical, tricksy ones being from the original source; see the diff without whitespace changes to make that more clear) but now there's a couple more. 😄
I added the benchmark test file I used (took some finagling to make it pass I also added a couple of lines to
I would hope As for the other wrongness, speed regressions: You could run the aforementioned benchmark for this module locally on the preimage and edited version, and see if something broke (more than ±10%; that seems to be the usual measurement noise margin). For a more reproducible and non-local approach: I've been working on speeding up Pillow, and codspeed was set up there (python-pillow/Pillow#9654) to run benchmarks during CI too. |
3f61951 to
61a9efd
Compare
|
@carltongibson Good instinct though - I did find a small pre-existing corner case bug that's separately fixed in #576 and this PR is now stacked on top of that commit. |
While profiling a Django app with viztracer, I noticed
asgirefLocal access/locking being actually visible in the trace, and figured there could be something to fix perf-wise :)@contextlib.contextmanagercan be slow due to the fact that the interpreter needs to juggle a generator frame, etc.This PR replaces it with a small real context manager (that is stored on the instance for reuse instead, in the first commit.The second commit further specializes
Local(using__new__magic, so this is transparent to consumers) to 2 classes depending on ifthread_criticalis set, so accesses do not require branches at all – and indeed, in the thread-critical case,nullcontextisn't required anymore. (Further, the guards in thesetattrcase become unnecessary since we can just useobject.__setattr__to sidestep the classes' impls.)EDIT: the third commit gets rid of the context manager altogether in favor of just inlining the same things as a
try:finally:block, as per comments here, for even more performance. 😄I pytest-benchmarked this to be pleasantly faster (and given that these are often very hot paths in Django, this is a nice win for not an awful lot of additional code):
Real context manager
test_getattr_thread_criticaltest_setattr_thread_criticaltest_getattr_defaulttest_setattr_defaulttest_delattr_thread_criticaltest_getattr_missing_defaulttest_delattr_defaulttest_getattr_thread_critical_asynctest_setattr_thread_critical_asyncNo context manager (only affects
default)