Uh oh!
There was an error while loading. Please reload this page.
For perf, use the new ConstructorInvoker APIs for ActivatorUtilities.CreateFactory - #89573
Conversation
ghost
commented
Jul 27, 2023
Tagging subscribers to this area: @dotnet/area-extensions-dependencyinjection Issue Details[verifying tests]
|
| [Fact] | ||
| public void CreateFactory_CreatesFactoryMethod() | ||
| public void CreateFactory_CreatesFactoryMethod_4Types_3Injected() |
There was a problem hiding this comment.
This new test was added for coverage; the other new code paths already had coverage.
eerhardt
left a comment
There was a problem hiding this comment.
Do we have any perf numbers (both throughput and size) for this change?
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.
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.
| if (serviceProvider is null) | ||
| ThrowHelperArgumentNullExceptionServiceProvider(); | ||
| object?[] constructorArguments = new object?[parameters.Length]; |
There was a problem hiding this comment.
Not saying we should but we could pool this array now that we have Span support.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
steveharter
commented
Aug 1, 2023
See #85065 for the previous size gains of removing Linq.Expressions assembly (went from ~260k+ to 4k) when See #66153 which has additional details on CPU gains. Reflection APIs are much faster in general, and the DI CreateFactory is ~1.5x faster depending on the scenario, but still slower than using Linq expressions. We could investigate further as the Blazor gains were not as much as the Windows gains, percentage-wise. |
| if (serviceProvider is null) | ||
| ThrowHelperArgumentNullExceptionServiceProvider(); |
There was a problem hiding this comment.
NIT: Since it is within .NET 8 if-def could we use ArgumentNullException.ThrowIfNull(serviceProvider)? Here and 4 similar cases below
There was a problem hiding this comment.
Yeah there was an earlier discussion around this - it's at least consistent now across all cases using the helper method. In one case, under .NET 8, we couldn't use ThrowIfNull due to a compile warning around unused variable (but we still want to verify for consistency) and of course under .NetStandard.NetFx we can't use ThrowIfNull at all.
buyaa-n
left a comment
There was a problem hiding this comment.
Left a NIT, overall LGTM thanks!
jkotas
commented
Aug 4, 2023
This change badly broke DI for native AOT. Number of DI tests are failing on native AOT in this repo, and it soon going to affect ASP.NET and perflab. I think we need to revert this change. |
Fixes#66153 which also contains additional information on benchmarks here.
This PR changes DI to use the new zero-alloc invoke APIs and is expected to close out the DI+Blazor perf work for v8 -- Blazor apps can use DI
ActivatorUtilities.CreateFactorywithout bringing in the largeSystem.Linq.Expressionsassembly and in a performant manner (but not quite as fast as having Blazor use Linq Expressions instead of reflection).In summary, for Blazor, this PR makes CreateFactory ~1.3-1.5x faster for common "fast paths" measured under a Blazor client app. However, when run under CorClr+Windows, there is a much larger ~2-3x gain. The difference appears to be additional overhead in Mono interpreter lambda methods with variable capture. For NativeAOT, this PR is expected to make CreateFactory also around ~1.5x faster based on reflection methods being 1.3x-1.7x faster with the new zero-alloc APIs.
Background:
System.Linq.Expressionsassembly.System.Linq.Expressionsassembly. Also reflection in general was changed to use emit under Blazor getting from 1.2x to 2x improvement depending on scenario (property setters and zero-arg ctor had the best perf gains).Note the reflection path is used (vs. Expressions) when
RuntimeFeature.IsDynamicCodeCompiled == falsewhich will be the case for Blazor client and NativeAOT . For NativeAOT, using expressions would cause them to be interpreted for NativeAOT which is very slow, and for Blazor using expressions would bring along the very large System.Linq.Expressions assembly where a smaller (trimmed) size is important. Note that this PR does not change the semantics here (that was done previously -- see the links above); only the CPU performance is improved.