-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Improve performance in DI #89964
Improve performance in DI #89964
Conversation
Tagging subscribers to this area: @dotnet/area-extensions-dependencyinjection Issue DetailsRelated issue: #89104 This PR manage to get some perf back, for example here are the results from Mode: Dynamic
Mode: Expressions
Mode: ILEmit
Mode: Runtime
|
@@ -573,26 +572,23 @@ private static CallSiteResultCacheLocation GetCommonCacheLocation(CallSiteResult | |||
{ | |||
ServiceCallSite? callSite = null; | |||
Type parameterType = parameters[index].ParameterType; | |||
if (parameters[index].CustomAttributes != null) | |||
foreach (var attribute in parameters[index].GetCustomAttributes(true)) |
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.
I assume getting all custom attributes is faster than 2 calls to get both FromKeyedServicesAttribute
and ServiceKeyAttribute
via the GetCustomAttribute(Type attributeType
overload?
I suppose another option is to consoldate to one attribute (API change) if GetCustomAttribute(Type attributeType
is much faster than getting all attributes.
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.
I already tried to make ServiceKeyAttribute
sealed
and use GetCustomAttribute(typeof(ServiceKeyAttribute)
(and only that, no trying to get the FromKeyedServicesAttribute
), and I don't see any difference, at least when there is no attributes at all:
Method | Job | Toolchain | Mode | Mean |
---|---|---|---|---|
Singleton | Job-GEPVBW | \runtime-fix\artifacts\bin\runtime\net8.0-windows-Release-x64\CoreRun.exe | Dynamic | 1.658 us |
Singleton | Job-OWDRFB | \runtime\artifacts\bin\runtime\net8.0-windows-Release-x64\CoreRun.exe | Dynamic | 1.753 us |
Singleton | Job-GEPVBW | \runtime-fix\artifacts\bin\runtime\net8.0-windows-Release-x64\CoreRun.exe | Expressions | 1.616 us |
Singleton | Job-OWDRFB | \runtime\artifacts\bin\runtime\net8.0-windows-Release-x64\CoreRun.exe | Expressions | 1.634 us |
Singleton | Job-GEPVBW | \runtime-fix\artifacts\bin\runtime\net8.0-windows-Release-x64\CoreRun.exe | ILEmit | 1.650 us |
Singleton | Job-OWDRFB | \runtime\artifacts\bin\runtime\net8.0-windows-Release-x64\CoreRun.exe | ILEmit | 1.646 us |
Singleton | Job-GEPVBW | \runtime-fix\artifacts\bin\runtime\net8.0-windows-Release-x64\CoreRun.exe | Runtime | 1.651 us |
Singleton | Job-OWDRFB | \runtime\artifacts\bin\runtime\net8.0-windows-Release-x64\CoreRun.exe | Runtime | 1.651 us |
@@ -54,7 +54,7 @@ public override int GetHashCode() | |||
} | |||
unchecked | |||
{ | |||
return ((ServiceType?.GetHashCode() ?? 23) * 397) ^ ServiceKey.GetHashCode(); | |||
return (ServiceType.GetHashCode() * 397) ^ ServiceKey.GetHashCode(); |
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.
ServiceType was never null?
In .NET 8 Preview 7, we noticed a regression in startup around: .NET 8 Preview 6: 45.35ms microsoft.maui!Microsoft.Maui.Hosting.MauiAppBuilder.Build() .NET 8 Preview 7: 62.17ms microsoft.maui!Microsoft.Maui.Hosting.MauiAppBuilder.Build() This may have been related to: dotnet/runtime#87183 Which should be improved by: dotnet/runtime#89964 In reviewing the traces, we noticed several places where MAUI was registering services like: services.AddTransient<IFoo, Foo>(); Where we could instead do: services.AddTransient<IFoo>(_ => new Foo()); Where this registration avoids any System.Reflection work at startup. Microsoft.Extensions.DI can just call the factory method instead. I expanded upon `BannedSymbols.txt` like we did in e7812b0, to ban cases of `AddTransient` and `AddScoped` that might be used accidentally. This resulted in banning the slow versions of these APIs like: Microsoft.Maui.Hosting.ImageSourceServiceCollectionExtensions.AddService Microsoft.Maui.Hosting.MauiHandlersCollectionExtensions.AddHandler Microsoft.Maui.Hosting.MauiHandlersCollectionExtensions.TryAddHandler I also introduced public, fast versions of methods in `MauiHandlersCollectionExtensions`. With this change in place, I could see the difference in: Before: 11.24ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine..ctor After: 6.29ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine..ctor Which resulted in an overall faster startup of a `dotnet new maui` app on a Pixel 5: Average(ms): 691 Std Err(ms): 3.00370142028502 Std Dev(ms): 9.49853789918334 Average(ms): 687.8 Std Err(ms): 4.04365071576553 Std Dev(ms): 12.7871463239892 This is an average of 10 runs. Note that this was built with main, and so we have an out-of-date AOT profile compared to the `net8.0` branch.
In .NET 8 Preview 7, we noticed a regression in startup around: .NET 8 Preview 6: 45.35ms microsoft.maui!Microsoft.Maui.Hosting.MauiAppBuilder.Build() .NET 8 Preview 7: 62.17ms microsoft.maui!Microsoft.Maui.Hosting.MauiAppBuilder.Build() This may have been related to: dotnet/runtime#87183 Which should be improved by: dotnet/runtime#89964 In reviewing the traces, we noticed several places where MAUI was registering services like: services.AddTransient<IFoo, Foo>(); Where we could instead do: services.AddTransient<IFoo>(_ => new Foo()); Where this registration avoids any System.Reflection work at startup. Microsoft.Extensions.DI can just call the factory method instead. I expanded upon `BannedSymbols.txt` like we did in e7812b0, to ban cases of `AddTransient` and `AddScoped` that might be used accidentally. This resulted in banning the slow versions of these APIs like: Microsoft.Maui.Hosting.ImageSourceServiceCollectionExtensions.AddService Microsoft.Maui.Hosting.MauiHandlersCollectionExtensions.AddHandler Microsoft.Maui.Hosting.MauiHandlersCollectionExtensions.TryAddHandler I also introduced public, fast versions of methods in `MauiHandlersCollectionExtensions`. With this change in place, I could see the difference in: Before: 11.24ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine..ctor After: 6.29ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine..ctor Which resulted in an overall faster startup of a `dotnet new maui` app on a Pixel 5: Average(ms): 691 Std Err(ms): 3.00370142028502 Std Dev(ms): 9.49853789918334 Average(ms): 687.8 Std Err(ms): 4.04365071576553 Std Dev(ms): 12.7871463239892 This is an average of 10 runs. Note that this was built with main, and so we have an out-of-date AOT profile compared to the `net8.0` branch.
In .NET 8 Preview 7, we noticed a regression in startup around: .NET 8 Preview 6: 45.35ms microsoft.maui!Microsoft.Maui.Hosting.MauiAppBuilder.Build() .NET 8 Preview 7: 62.17ms microsoft.maui!Microsoft.Maui.Hosting.MauiAppBuilder.Build() This may have been related to: dotnet/runtime#87183 Which should be improved by: dotnet/runtime#89964 In reviewing the traces, we noticed several places where MAUI was registering services like: services.AddTransient<IFoo, Foo>(); Where we could instead do: services.AddTransient<IFoo>(_ => new Foo()); Where this registration avoids any System.Reflection work at startup. Microsoft.Extensions.DI can just call the factory method instead. I expanded upon `BannedSymbols.txt` like we did in e7812b0, to ban cases of `AddTransient` and `AddScoped` that might be used accidentally. This resulted in banning the slow versions of these APIs like: Microsoft.Maui.Hosting.ImageSourceServiceCollectionExtensions.AddService Microsoft.Maui.Hosting.MauiHandlersCollectionExtensions.AddHandler Microsoft.Maui.Hosting.MauiHandlersCollectionExtensions.TryAddHandler I also introduced public, fast versions of methods in `MauiHandlersCollectionExtensions`. With this change in place, I could see the difference in: Before: 11.24ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine..ctor After: 6.29ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine..ctor Which resulted in an overall faster startup of a `dotnet new maui` app on a Pixel 5: Average(ms): 691 Std Err(ms): 3.00370142028502 Std Dev(ms): 9.49853789918334 Average(ms): 687.8 Std Err(ms): 4.04365071576553 Std Dev(ms): 12.7871463239892 This is an average of 10 runs. Note that this was built with main, and so we have an out-of-date AOT profile compared to the `net8.0` branch.
In .NET 8 Preview 7, we noticed a regression in startup around: .NET 8 Preview 6: 45.35ms microsoft.maui!Microsoft.Maui.Hosting.MauiAppBuilder.Build() .NET 8 Preview 7: 62.17ms microsoft.maui!Microsoft.Maui.Hosting.MauiAppBuilder.Build() This may have been related to: dotnet/runtime#87183 Which should be improved by: dotnet/runtime#89964 In reviewing the traces, we noticed several places where MAUI was registering services like: services.AddTransient<IFoo, Foo>(); Where we could instead do: services.AddTransient<IFoo>(_ => new Foo()); Where this registration avoids any System.Reflection work at startup. Microsoft.Extensions.DI can just call the factory method instead. I expanded upon `BannedSymbols.txt` like we did in e7812b0, to ban cases of `AddTransient` and `AddScoped` that might be used accidentally. This resulted in banning the slow versions of these APIs like: Microsoft.Maui.Hosting.ImageSourceServiceCollectionExtensions.AddService Microsoft.Maui.Hosting.MauiHandlersCollectionExtensions.AddHandler Microsoft.Maui.Hosting.MauiHandlersCollectionExtensions.TryAddHandler I also introduced public, fast versions of methods in `MauiHandlersCollectionExtensions`. With this change in place, I could see the difference in: Before: 11.24ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine..ctor After: 6.29ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine..ctor Which resulted in an overall faster startup of a `dotnet new maui` app on a Pixel 5: Average(ms): 691 Std Err(ms): 3.00370142028502 Std Dev(ms): 9.49853789918334 Average(ms): 687.8 Std Err(ms): 4.04365071576553 Std Dev(ms): 12.7871463239892 This is an average of 10 runs. Note that this was built with main, and so we have an out-of-date AOT profile compared to the `net8.0` branch.
In .NET 8 Preview 7, we noticed a regression in startup around: .NET 8 Preview 6: 45.35ms microsoft.maui!Microsoft.Maui.Hosting.MauiAppBuilder.Build() .NET 8 Preview 7: 62.17ms microsoft.maui!Microsoft.Maui.Hosting.MauiAppBuilder.Build() This may have been related to: dotnet/runtime#87183 Which should be improved by: dotnet/runtime#89964 In reviewing the traces, we noticed several places where MAUI was registering services like: services.AddTransient<IFoo, Foo>(); Where we could instead do: services.AddTransient<IFoo>(_ => new Foo()); Where this registration avoids any System.Reflection work at startup. Microsoft.Extensions.DI can just call the factory method instead. I expanded upon `BannedSymbols.txt` like we did in e7812b0, to ban cases of `AddTransient` and `AddScoped` that might be used accidentally. This resulted in banning the slow versions of these APIs like: Microsoft.Maui.Hosting.ImageSourceServiceCollectionExtensions.AddService Microsoft.Maui.Hosting.MauiHandlersCollectionExtensions.AddHandler Microsoft.Maui.Hosting.MauiHandlersCollectionExtensions.TryAddHandler I also introduced public, fast versions of methods in `MauiHandlersCollectionExtensions`. With this change in place, I could see the difference in: Before: 11.24ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine..ctor After: 6.29ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine..ctor Which resulted in an overall faster startup of a `dotnet new maui` app on a Pixel 5: Average(ms): 691 Std Err(ms): 3.00370142028502 Std Dev(ms): 9.49853789918334 Average(ms): 687.8 Std Err(ms): 4.04365071576553 Std Dev(ms): 12.7871463239892 This is an average of 10 runs. Note that this was built with main, and so we have an out-of-date AOT profile compared to the `net8.0` branch.
* [core] use factory methods for registering services In .NET 8 Preview 7, we noticed a regression in startup around: .NET 8 Preview 6: 45.35ms microsoft.maui!Microsoft.Maui.Hosting.MauiAppBuilder.Build() .NET 8 Preview 7: 62.17ms microsoft.maui!Microsoft.Maui.Hosting.MauiAppBuilder.Build() This may have been related to: dotnet/runtime#87183 Which should be improved by: dotnet/runtime#89964 In reviewing the traces, we noticed several places where MAUI was registering services like: services.AddTransient<IFoo, Foo>(); Where we could instead do: services.AddTransient<IFoo>(_ => new Foo()); Where this registration avoids any System.Reflection work at startup. Microsoft.Extensions.DI can just call the factory method instead. I expanded upon `BannedSymbols.txt` like we did in e7812b0, to ban cases of `AddTransient` and `AddScoped` that might be used accidentally. This resulted in banning the slow versions of these APIs like: Microsoft.Maui.Hosting.ImageSourceServiceCollectionExtensions.AddService Microsoft.Maui.Hosting.MauiHandlersCollectionExtensions.AddHandler Microsoft.Maui.Hosting.MauiHandlersCollectionExtensions.TryAddHandler I also introduced public, fast versions of methods in `MauiHandlersCollectionExtensions`. With this change in place, I could see the difference in: Before: 11.24ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine..ctor After: 6.29ms microsoft.extensions.dependencyinjection!Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine..ctor Which resulted in an overall faster startup of a `dotnet new maui` app on a Pixel 5: Average(ms): 691 Std Err(ms): 3.00370142028502 Std Dev(ms): 9.49853789918334 Average(ms): 687.8 Std Err(ms): 4.04365071576553 Std Dev(ms): 12.7871463239892 This is an average of 10 runs. Note that this was built with main, and so we have an out-of-date AOT profile compared to the `net8.0` branch. * Remove CellRenderer registration
Related issue: #89104
This PR manage to get some perf back, for example here are the results from
TimeToFirstService
tests on my machine:Mode: Dynamic
Mode: Expressions
Mode: ILEmit
Mode: Runtime