Skip to content
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

Merged
merged 3 commits into from
Aug 7, 2023
Merged

Conversation

benjaminpetit
Copy link
Member

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

Type Method Toolchain Mean Error StdDev Median Min Max Gen0 Gen1 Allocated
TimeToFirstService BuildProvider before 753.182 ns 8.1624 ns 7.2357 ns 753.700 ns 739.973 ns 764.742 ns 0.4668 0.0091 6128 B
TimeToFirstService Transient before 1,850.161 ns 13.2022 ns 11.7034 ns 1,848.994 ns 1,819.342 ns 1,870.730 ns 0.5957 0.0147 7832 B
TimeToFirstService Scoped before 2,254.959 ns 99.2547 ns 106.2014 ns 2,216.799 ns 2,147.805 ns 2,440.183 ns 0.6297 - 8592 B
TimeToFirstService Singleton before 1,814.500 ns 35.9730 ns 30.0391 ns 1,826.555 ns 1,768.116 ns 1,861.855 ns 0.5689 - 7752 B
TimeToFirstService BuildProvider after 735.550 ns 7.7966 ns 6.9115 ns 735.079 ns 724.098 ns 750.727 ns 0.4637 0.0087 6064 B
TimeToFirstService Transient after 1,450.943 ns 13.5098 ns 11.9761 ns 1,448.951 ns 1,434.024 ns 1,472.397 ns 0.5851 0.0116 7720 B
TimeToFirstService Scoped after 1,628.323 ns 29.2638 ns 25.9416 ns 1,623.571 ns 1,587.373 ns 1,681.996 ns 0.6397 0.0129 8424 B
TimeToFirstService Singleton after 1,479.134 ns 10.9894 ns 10.2795 ns 1,479.818 ns 1,461.227 ns 1,497.052 ns 0.5824 0.0118 7640 B

Mode: Expressions

Type Method Toolchain Mean Error StdDev Median Min Max Gen0 Gen1 Allocated
TimeToFirstService BuildProvider before 748.165 ns 10.0476 ns 8.9070 ns 748.712 ns 734.724 ns 768.351 ns 0.4687 0.0089 6128 B
TimeToFirstService Transient before 1,727.041 ns 19.5083 ns 18.2481 ns 1,723.208 ns 1,703.159 ns 1,765.775 ns 0.5993 0.0139 7832 B
TimeToFirstService Scoped before 2,085.410 ns 20.9439 ns 19.5910 ns 2,081.465 ns 2,052.385 ns 2,124.829 ns 0.6511 0.0165 8592 B
TimeToFirstService Singleton before 1,714.011 ns 8.9126 ns 7.9008 ns 1,712.218 ns 1,701.574 ns 1,725.505 ns 0.5909 0.0139 7752 B
TimeToFirstService BuildProvider after 704.607 ns 7.6209 ns 6.7558 ns 706.088 ns 693.590 ns 713.942 ns 0.4627 0.0084 6064 B
TimeToFirstService Transient after 1,464.115 ns 16.4037 ns 15.3440 ns 1,461.208 ns 1,445.253 ns 1,492.207 ns 0.5855 0.0117 7720 B
TimeToFirstService Scoped after 1,655.321 ns 19.3172 ns 17.1242 ns 1,651.134 ns 1,637.538 ns 1,692.870 ns 0.6400 0.0129 8424 B
TimeToFirstService Singleton after 1,477.645 ns 20.9259 ns 19.5741 ns 1,472.233 ns 1,454.302 ns 1,517.361 ns 0.5799 0.0118 7640 B

Mode: ILEmit

Type Method Toolchain Mean Error StdDev Median Min Max Gen0 Gen1 Allocated
TimeToFirstService BuildProvider before 747.465 ns 8.3819 ns 7.8404 ns 745.817 ns 736.959 ns 762.628 ns 0.4659 0.0090 6128 B
TimeToFirstService Transient before 1,850.115 ns 29.8682 ns 27.9388 ns 1,845.605 ns 1,802.040 ns 1,889.587 ns 0.5952 0.0147 7832 B
TimeToFirstService Scoped before 2,071.536 ns 16.4799 ns 15.4153 ns 2,075.149 ns 2,046.785 ns 2,104.762 ns 0.6574 0.0164 8592 B
TimeToFirstService Singleton before 1,729.107 ns 21.5811 ns 20.1870 ns 1,727.140 ns 1,696.679 ns 1,773.935 ns 0.5896 0.0137 7752 B
TimeToFirstService BuildProvider after 710.039 ns 8.3128 ns 7.7758 ns 709.738 ns 695.468 ns 723.130 ns 0.4631 0.0085 6064 B
TimeToFirstService Transient after 1,600.915 ns 17.8740 ns 14.9256 ns 1,599.622 ns 1,581.591 ns 1,638.828 ns 0.5888 0.0128 7720 B
TimeToFirstService Scoped after 1,634.161 ns 16.9376 ns 15.0147 ns 1,639.237 ns 1,607.226 ns 1,654.997 ns 0.6430 0.0130 8424 B
TimeToFirstService Singleton after 1,497.916 ns 14.4069 ns 13.4762 ns 1,496.076 ns 1,476.665 ns 1,522.015 ns 0.5790 0.0118 7640 B

Mode: Runtime

Type Method Toolchain Mean Error StdDev Median Min Max Gen0 Gen1 Allocated
TimeToFirstService BuildProvider before 762.557 ns 7.6228 ns 7.1304 ns 764.343 ns 753.039 ns 776.406 ns 0.4675 0.0091 6128 B
TimeToFirstService Transient before 1,865.525 ns 18.0385 ns 15.9907 ns 1,862.359 ns 1,846.681 ns 1,899.738 ns 0.5963 0.0149 7832 B
TimeToFirstService Scoped before 2,072.554 ns 17.1927 ns 16.0821 ns 2,068.519 ns 2,054.988 ns 2,100.529 ns 0.6532 0.0165 8592 B
TimeToFirstService Singleton before 1,741.121 ns 13.3652 ns 12.5018 ns 1,743.684 ns 1,721.771 ns 1,764.339 ns 0.5902 0.0139 7752 B
TimeToFirstService BuildProvider after 702.085 ns 9.2328 ns 8.6364 ns 700.056 ns 687.292 ns 717.406 ns 0.4622 0.0085 6064 B
TimeToFirstService Transient after 1,478.332 ns 20.7039 ns 19.3664 ns 1,474.176 ns 1,450.284 ns 1,516.376 ns 0.5850 0.0117 7720 B
TimeToFirstService Scoped after 1,629.725 ns 13.7779 ns 12.2137 ns 1,626.608 ns 1,613.067 ns 1,655.360 ns 0.6392 0.0129 8424 B
TimeToFirstService Singleton after 1,507.486 ns 17.7877 ns 15.7684 ns 1,505.385 ns 1,486.664 ns 1,544.344 ns 0.5839 0.0122 7640 B

@ghost
Copy link

ghost commented Aug 3, 2023

Tagging subscribers to this area: @dotnet/area-extensions-dependencyinjection
See info in area-owners.md if you want to be subscribed.

Issue Details

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

Type Method Toolchain Mean Error StdDev Median Min Max Gen0 Gen1 Allocated
TimeToFirstService BuildProvider before 753.182 ns 8.1624 ns 7.2357 ns 753.700 ns 739.973 ns 764.742 ns 0.4668 0.0091 6128 B
TimeToFirstService Transient before 1,850.161 ns 13.2022 ns 11.7034 ns 1,848.994 ns 1,819.342 ns 1,870.730 ns 0.5957 0.0147 7832 B
TimeToFirstService Scoped before 2,254.959 ns 99.2547 ns 106.2014 ns 2,216.799 ns 2,147.805 ns 2,440.183 ns 0.6297 - 8592 B
TimeToFirstService Singleton before 1,814.500 ns 35.9730 ns 30.0391 ns 1,826.555 ns 1,768.116 ns 1,861.855 ns 0.5689 - 7752 B
TimeToFirstService BuildProvider after 735.550 ns 7.7966 ns 6.9115 ns 735.079 ns 724.098 ns 750.727 ns 0.4637 0.0087 6064 B
TimeToFirstService Transient after 1,450.943 ns 13.5098 ns 11.9761 ns 1,448.951 ns 1,434.024 ns 1,472.397 ns 0.5851 0.0116 7720 B
TimeToFirstService Scoped after 1,628.323 ns 29.2638 ns 25.9416 ns 1,623.571 ns 1,587.373 ns 1,681.996 ns 0.6397 0.0129 8424 B
TimeToFirstService Singleton after 1,479.134 ns 10.9894 ns 10.2795 ns 1,479.818 ns 1,461.227 ns 1,497.052 ns 0.5824 0.0118 7640 B

Mode: Expressions

Type Method Toolchain Mean Error StdDev Median Min Max Gen0 Gen1 Allocated
TimeToFirstService BuildProvider before 748.165 ns 10.0476 ns 8.9070 ns 748.712 ns 734.724 ns 768.351 ns 0.4687 0.0089 6128 B
TimeToFirstService Transient before 1,727.041 ns 19.5083 ns 18.2481 ns 1,723.208 ns 1,703.159 ns 1,765.775 ns 0.5993 0.0139 7832 B
TimeToFirstService Scoped before 2,085.410 ns 20.9439 ns 19.5910 ns 2,081.465 ns 2,052.385 ns 2,124.829 ns 0.6511 0.0165 8592 B
TimeToFirstService Singleton before 1,714.011 ns 8.9126 ns 7.9008 ns 1,712.218 ns 1,701.574 ns 1,725.505 ns 0.5909 0.0139 7752 B
TimeToFirstService BuildProvider after 704.607 ns 7.6209 ns 6.7558 ns 706.088 ns 693.590 ns 713.942 ns 0.4627 0.0084 6064 B
TimeToFirstService Transient after 1,464.115 ns 16.4037 ns 15.3440 ns 1,461.208 ns 1,445.253 ns 1,492.207 ns 0.5855 0.0117 7720 B
TimeToFirstService Scoped after 1,655.321 ns 19.3172 ns 17.1242 ns 1,651.134 ns 1,637.538 ns 1,692.870 ns 0.6400 0.0129 8424 B
TimeToFirstService Singleton after 1,477.645 ns 20.9259 ns 19.5741 ns 1,472.233 ns 1,454.302 ns 1,517.361 ns 0.5799 0.0118 7640 B

Mode: ILEmit

Type Method Toolchain Mean Error StdDev Median Min Max Gen0 Gen1 Allocated
TimeToFirstService BuildProvider before 747.465 ns 8.3819 ns 7.8404 ns 745.817 ns 736.959 ns 762.628 ns 0.4659 0.0090 6128 B
TimeToFirstService Transient before 1,850.115 ns 29.8682 ns 27.9388 ns 1,845.605 ns 1,802.040 ns 1,889.587 ns 0.5952 0.0147 7832 B
TimeToFirstService Scoped before 2,071.536 ns 16.4799 ns 15.4153 ns 2,075.149 ns 2,046.785 ns 2,104.762 ns 0.6574 0.0164 8592 B
TimeToFirstService Singleton before 1,729.107 ns 21.5811 ns 20.1870 ns 1,727.140 ns 1,696.679 ns 1,773.935 ns 0.5896 0.0137 7752 B
TimeToFirstService BuildProvider after 710.039 ns 8.3128 ns 7.7758 ns 709.738 ns 695.468 ns 723.130 ns 0.4631 0.0085 6064 B
TimeToFirstService Transient after 1,600.915 ns 17.8740 ns 14.9256 ns 1,599.622 ns 1,581.591 ns 1,638.828 ns 0.5888 0.0128 7720 B
TimeToFirstService Scoped after 1,634.161 ns 16.9376 ns 15.0147 ns 1,639.237 ns 1,607.226 ns 1,654.997 ns 0.6430 0.0130 8424 B
TimeToFirstService Singleton after 1,497.916 ns 14.4069 ns 13.4762 ns 1,496.076 ns 1,476.665 ns 1,522.015 ns 0.5790 0.0118 7640 B

Mode: Runtime

Type Method Toolchain Mean Error StdDev Median Min Max Gen0 Gen1 Allocated
TimeToFirstService BuildProvider before 762.557 ns 7.6228 ns 7.1304 ns 764.343 ns 753.039 ns 776.406 ns 0.4675 0.0091 6128 B
TimeToFirstService Transient before 1,865.525 ns 18.0385 ns 15.9907 ns 1,862.359 ns 1,846.681 ns 1,899.738 ns 0.5963 0.0149 7832 B
TimeToFirstService Scoped before 2,072.554 ns 17.1927 ns 16.0821 ns 2,068.519 ns 2,054.988 ns 2,100.529 ns 0.6532 0.0165 8592 B
TimeToFirstService Singleton before 1,741.121 ns 13.3652 ns 12.5018 ns 1,743.684 ns 1,721.771 ns 1,764.339 ns 0.5902 0.0139 7752 B
TimeToFirstService BuildProvider after 702.085 ns 9.2328 ns 8.6364 ns 700.056 ns 687.292 ns 717.406 ns 0.4622 0.0085 6064 B
TimeToFirstService Transient after 1,478.332 ns 20.7039 ns 19.3664 ns 1,474.176 ns 1,450.284 ns 1,516.376 ns 0.5850 0.0117 7720 B
TimeToFirstService Scoped after 1,629.725 ns 13.7779 ns 12.2137 ns 1,626.608 ns 1,613.067 ns 1,655.360 ns 0.6392 0.0129 8424 B
TimeToFirstService Singleton after 1,507.486 ns 17.7877 ns 15.7684 ns 1,505.385 ns 1,486.664 ns 1,544.344 ns 0.5839 0.0122 7640 B
Author: benjaminpetit
Assignees: -
Labels:

area-Extensions-DependencyInjection

Milestone: -

@@ -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))
Copy link
Member

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.

Copy link
Member Author

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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ServiceType was never null?

@benjaminpetit benjaminpetit merged commit 6029c30 into dotnet:main Aug 7, 2023
103 checks passed
jonathanpeppers added a commit to jonathanpeppers/maui that referenced this pull request Aug 14, 2023
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.
jonathanpeppers added a commit to jonathanpeppers/maui that referenced this pull request Aug 15, 2023
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.
jonathanpeppers added a commit to jonathanpeppers/maui that referenced this pull request Aug 16, 2023
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.
jonathanpeppers added a commit to jonathanpeppers/maui that referenced this pull request Aug 17, 2023
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.
jonathanpeppers added a commit to jonathanpeppers/maui that referenced this pull request Aug 22, 2023
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.
rmarinho pushed a commit to dotnet/maui that referenced this pull request Aug 23, 2023
* [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
@ghost ghost locked as resolved and limited conversation to collaborators Sep 29, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants