-
Notifications
You must be signed in to change notification settings - Fork 11k
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
[11.x] Optimize boostrap time by using hashtable to store providers #51343
Conversation
Maybe I'm not understanding something here, but isn't the point of having separate "registered" and "loaded" service providers so we can defer loading of ones that we won't necessarily need on every request? Does this change maintain that functionality? |
@browner12 if you mean deferred providers, those changes don't touch that functionality directly. Deferred providers are in the property: deferredServices loadProviders property was used only in the method: markAsRegistered, where a provider is added to serviceProviders, so it looks like using only one array as the source of truth should be ok. |
Can you share benchmarks of speed improvements on a blank Laravel application that makes a single database query? |
Hasn't logic changed now? Because |
@taylorotwell A fresh application contains a small number of providers, so it isn't even shown by Blackfire as a significant cost. However, I've added 300 providers to some fresh app, and here are the results: BEFORE After my change, Blackfire doesn't show that method, because the cost is insignificant. There are ((n - 1) * n) / 2 of executions, so for small apps, it isn't important, but for bigger applications with more providers like 100, 200, 300, or even more it should be a nice optimization having an impact on every request. @bert-w it shouldn't be a problem for getProvider method, as there is always returned only one. The method getProviders returns results in the same way comparing by instanceof, which is used in some places to get e.g. all events providers by the base class. Or maybe, I overlooked something? |
Remember that Taylor doesn't sees draft PR's. Please mark this as ready if you need another review. |
@driesvints thanks for clarifying @taylorotwell could you have another look? I'm not sure if you received a notification about my response, because it was written when the PR was still marked as a draft. |
Sorry, maybe I'm missing it, but how long does it take to run an entire Laravel request (in milliseconds) for a normal app with a single database query before and after this PR? |
@taylorotwell As I've written, this isn't relevant for small apps with only a few providers. So I can't prepare such a comparison for "a normal app with one database query", because it isn't possible to show any optimization in that case. It would be useful only for larger apps with many providers. I've seen some monolithic apps using Laravel with over 300 providers, so this isn't unusual. Here is a comparison of an app with 300 providers:
BEFORE
AFTER
BTW It is hard to measure fairly, because the difference is quite small 2-3ms per request, so the best way to present that optimization is to look at the profiler (screens in my previous comment). |
@sarven sorry to drag this PR out a bit, but is it at all possible to make the relevant performance improvements with minimal others changes? For example while keeping the |
fa5b162
to
19b3671
Compare
@taylorotwell no problem, changed. I thought also about creating a new private method like getProviderByClassName to keep the behavior of the current getProvider the same. Currently, for example, it's possible to get the first provider of the base class like ServiceProvider, because there is used instanceof, but it doesn't seem like something useful and I assume that no one uses that method in that way, so changing the behavior a little bit shouldn't be a problem. What do you think? |
It should slightly optimize bootstrap time, especially for large applications with many providers. I've encountered that the method getProvider has O(n) complexity, but it could be optimized to O(1).
Of course, this time from Blackfire is not true, and the original value is smaller, but still executing that function ~63k times isn't a good thing.
Seems like serviceProviders hashtable is enough, and we can get rid of loadedProviders.