Skip to content

Have ConstructorInfo use Activator.CreateInstance() instead of emit for default constructors #78917

@steveharter

Description

@steveharter

Activator.CreateInstance() was optimized in v6 and made super fast when invoking the default constructor. As shown in the benchmarks below, currently calling a zero-parameter constructor through Activator.CreateInstance(...) is ~1.7x faster than ConstructorInfo. Note that before the 7.0 feature to use IL emit, Activator.CreateInstance(...) used to be ~6x faster than ConstructorInfo.

|                                                                       Method |        Job |              Toolchain |       Mean |      Error |     StdDev |     Median |        Min |        Max | Ratio | RatioSD |  Gen 0 | Allocated | Alloc Ratio |
|----------------------------------------------------------------------------- |----------- |----------------------- |-----------:|-----------:|-----------:|-----------:|-----------:|-----------:|------:|--------:|-------:|----------:|------------:|
|                                       Ctor0_ActivatorCreateInstance_NoParams | Job-VAFQFP | \newinvoke\corerun.exe |   8.700 ns |  0.1857 ns |  0.1646 ns |   8.733 ns |   8.395 ns |   8.999 ns |  1.00 |    0.00 | 0.0054 |      56 B |        1.00 |
|                                                               Ctor0_NoParams | Job-VAFQFP | \newinvoke\corerun.exe |  14.928 ns |  0.5837 ns |  0.6721 ns |  14.697 ns |  14.234 ns |  16.242 ns |  1.00 |    0.00 | 0.0053 |      56 B |        1.00 |

Although Activator is faster than ConstructorInfo that only applies for cases when there is a default constructor. When there are constructor parameters, calling ConstructorInfo is up to 2x faster.

Due to the varying performance of these two APIs, reflection users that need to invoke constructors and want the maximum performance currently need to call ConstructorInfo when there are parameters and Activator when there are no parameters. This issue should make this choice go away by making ConstructorInfo the preferred API going forward for almost all cases by having ConstructorInfo call Activator for default constructors. This also avoids generating IL for these cases which can help with start-up and reduce global memory usage.

Using ConstructorInfo instead of Activator also has the advantage of forcing the caller select the appropriate constructor ahead of time, instead of auto-selecting each time the method is called by inspecting the parameters. If the caller does not know what constructor to call, then continuing to use Activator is fine.

We also need to look at normalizing the exception handling between these two APIs so they have the same Exception semantics for OutOfMemoryException.

Also see #36194 for ideas on exposing "Factory" overloads that will not throw `TargetInvocationException".

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions