-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[EventDispatcher][VarDumper] optimize perf by leveraging Closure::fromCallable() #29245
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
Conversation
0683f6c
to
f157a24
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this not incorporated inside sortListeners function? Instead it repeats the logic but ignores sort marking and is used only during dispatching
This doesn't affect performance for run-once listeners.
How come? It makes extra operations (new closure, Closure::fromCallable call)
f157a24
to
2b9d9c8
Compare
@ostrolucky thanks for the review!
Because sortListeners has public side effect: it drive the return value of
and removes some. I did benchmark this, there is no measurable difference. |
I didn't mean to to imply you should save the optimization result into |
2b9d9c8
to
95eb251
Compare
that could defeat the extra laziness added here (not instantiating lazy listeners when propagation is stopped before them) |
5a9ac14
to
5bc59f1
Compare
I don't see how would it defeat it 🤔 Only way that closure is replaced with delegated one is when it's executed, which will happen only during dispatching. |
Because of the |
5bc59f1
to
719e602
Compare
I get that, but that line is executed only when outer closure is executed. I don't see why would moving construction of this closure to different place change when is it executed |
Because we have to call that line to populate the sortedListeners property. |
This closure trick increases speed by 0.6μs aggregated (8%). 0.15μs from that is just thanks to inlining getListeners call. So closure trick alone is 0.45μs. Measured with phpbench, 100 listeners attached (and all of them executed). Correlation on number of listeners is very linear. I don't think this is worth increased complexity and number of untested paths. Also, is there a PHP bug report? I think this is something better to be improved on language level. |
We made current Symfony versions really fast because we made such changes. Small improvements for your bench can be a significant one for a specific use case. And it's already been proven that many small improvements aggregate to significant general ones. The real issue is having EventDispatcher mutable. We should inspect existing use cases where this is needed (if any) and consider deprecating if possible IMHO. But that shouldn't block this one. |
Well if we decide it's worth it, can you at least properly comment this code, cover it by tests and notify upstream about workarounds we need to do to increase performance so there is at least some chance we can eventually remove them? This was proven successful with eg inlining constants in DI Container. Performance tweaks like this are killing readability and hence ability to debug and contribute a lot. As you can see from my questions there are lot of non-obvious things for other people. I still don't know in what case would |
that part is the lazy-loading of the listener object. |
I see that, I just don't see how can be listener modified outside of this proxy closure. But testcase would help me with that wink wink |
446270e
to
68f3b51
Compare
The discussed code path is now tested @ostrolucky, testMutatingWhilePropagationIsStopped. Don't be fooled by the diff stat, I just renamed |
Thanks. I can see now that same closure is evaluated by sortListeners and optimizeListeners might not re-triggered after that happens. |
68f3b51
to
d6a594b
Compare
…ariables (ostrolucky) This PR was merged into the 4.1 branch. Discussion ---------- Optimize perf by replacing call_user_func with dynamic variables | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | This provides similar boost as in #29245, but on more places and without complexity increase. Check eg. https://github.com/fab2s/call_user_func for proof Fabpot failure unrelated Commits ------- 0c6ef01 Optimize perf by replacing call_user_func with dynamic vars
…ariables (ostrolucky) This PR was merged into the 4.1 branch. Discussion ---------- Optimize perf by replacing call_user_func with dynamic variables | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | This provides similar boost as in symfony/symfony#29245, but on more places and without complexity increase. Check eg. https://github.com/fab2s/call_user_func for proof Fabpot failure unrelated Commits ------- 0c6ef01713 Optimize perf by replacing call_user_func with dynamic vars
…ariables (ostrolucky) This PR was merged into the 4.1 branch. Discussion ---------- Optimize perf by replacing call_user_func with dynamic variables | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | This provides similar boost as in symfony/symfony#29245, but on more places and without complexity increase. Check eg. https://github.com/fab2s/call_user_func for proof Fabpot failure unrelated Commits ------- 0c6ef01713 Optimize perf by replacing call_user_func with dynamic vars
…ariables (ostrolucky) This PR was merged into the 4.1 branch. Discussion ---------- Optimize perf by replacing call_user_func with dynamic variables | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | This provides similar boost as in symfony/symfony#29245, but on more places and without complexity increase. Check eg. https://github.com/fab2s/call_user_func for proof Fabpot failure unrelated Commits ------- 0c6ef01713 Optimize perf by replacing call_user_func with dynamic vars
…ariables (ostrolucky) This PR was merged into the 4.1 branch. Discussion ---------- Optimize perf by replacing call_user_func with dynamic variables | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | This provides similar boost as in symfony/symfony#29245, but on more places and without complexity increase. Check eg. https://github.com/fab2s/call_user_func for proof Fabpot failure unrelated Commits ------- 0c6ef01713 Optimize perf by replacing call_user_func with dynamic vars
…ariables (ostrolucky) This PR was merged into the 4.1 branch. Discussion ---------- Optimize perf by replacing call_user_func with dynamic variables | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | This provides similar boost as in symfony/symfony#29245, but on more places and without complexity increase. Check eg. https://github.com/fab2s/call_user_func for proof Fabpot failure unrelated Commits ------- 0c6ef01 Optimize perf by replacing call_user_func with dynamic vars
Thank you @nicolas-grekas. |
… Closure::fromCallable() (nicolas-grekas) This PR was merged into the 4.3-dev branch. Discussion ---------- [EventDispatcher][VarDumper] optimize perf by leveraging Closure::fromCallable() | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - Callables are notably slower than closures. Let's turn then to closures thanks to `Closure::fromCallable()`. This doesn't affect performance for run-once listeners. And improves performance for events that are dispatched several times. Same for VarDumper's casters. Commits ------- d6a594b [EventDispatcher][VarDumper] optimize perf by leveraging Closure::fromCallable()
Callables are notably slower than closures. Let's turn then to closures thanks to
Closure::fromCallable()
.This doesn't affect performance for run-once listeners.
And improves performance for events that are dispatched several times.
Same for VarDumper's casters.