Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork9.7k
[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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
0683f6c tof157a24Compare
ostrolucky left a comment
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)
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
f157a24 to2b9d9c8Comparenicolas-grekas commentedNov 18, 2018
@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. |
ostrolucky commentedNov 18, 2018
I didn't mean to to imply you should save the optimization result into |
2b9d9c8 to95eb251Comparenicolas-grekas commentedNov 18, 2018
that could defeat the extra laziness added here (not instantiating lazy listeners when propagation is stopped before them) |
5a9ac14 to5bc59f1Compareostrolucky commentedNov 18, 2018
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. |
nicolas-grekas commentedNov 18, 2018
Because of the |
5bc59f1 to719e602Compareostrolucky commentedNov 18, 2018
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 |
nicolas-grekas commentedNov 18, 2018
Because we have to call that line to populate the sortedListeners property. |
Uh oh!
There was an error while loading.Please reload this page.
ostrolucky commentedNov 19, 2018 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
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. |
nicolas-grekas commentedNov 20, 2018 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
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. |
ostrolucky commentedNov 20, 2018
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 |
stof commentedNov 20, 2018
that part is the lazy-loading of the listener object. |
ostrolucky commentedNov 20, 2018
I see that, I just don't see how can be listener modified outside of this proxy closure. But testcase would help me with thatwink wink |
446270e to68f3b51Comparenicolas-grekas commentedNov 21, 2018
The discussed code path is now tested@ostrolucky, testMutatingWhilePropagationIsStopped. Don't be fooled by the diff stat, I just renamed |
ostrolucky commentedNov 24, 2018
Thanks. I can see now that same closure is evaluated by sortListeners and optimizeListeners might not re-triggered after that happens. |
68f3b51 tod6a594bCompare…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 proofFabpot failure unrelatedCommits-------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 insymfony/symfony#29245, but on more places and without complexity increase. Check eg.https://github.com/fab2s/call_user_func for proofFabpot failure unrelatedCommits-------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 insymfony/symfony#29245, but on more places and without complexity increase. Check eg.https://github.com/fab2s/call_user_func for proofFabpot failure unrelatedCommits-------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 insymfony/symfony#29245, but on more places and without complexity increase. Check eg.https://github.com/fab2s/call_user_func for proofFabpot failure unrelatedCommits-------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 insymfony/symfony#29245, but on more places and without complexity increase. Check eg.https://github.com/fab2s/call_user_func for proofFabpot failure unrelatedCommits-------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 insymfony/symfony#29245, but on more places and without complexity increase. Check eg.https://github.com/fab2s/call_user_func for proofFabpot failure unrelatedCommits-------0c6ef01 Optimize perf by replacing call_user_func with dynamic vars
fabpot commentedDec 10, 2018
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.