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 of Activator.CreateInstance #32520

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
d0bde83
Fast object instantiation, take two
GrabYourPitchforks May 9, 2020
67539c2
Merge remote-tracking branch 'origin/master' into fast_createinstance…
GrabYourPitchforks Jul 8, 2020
edf3858
PR feedback
GrabYourPitchforks Jul 7, 2020
c5ae79f
Re-enable analyzers in project
GrabYourPitchforks Jul 8, 2020
e09e91b
Don't run COM tests on non-Windows
GrabYourPitchforks Jul 8, 2020
17a3bde
PR feedback
GrabYourPitchforks Jul 8, 2020
2467fdd
Fix mergeMerge remote-tracking branch 'origin/master' into fast_creat…
GrabYourPitchforks Nov 17, 2020
eb368eb
Cleanup + PR feedback
GrabYourPitchforks Nov 17, 2020
b85fb74
Merge remote-tracking branch 'origin/master' into fast_createinstance
GrabYourPitchforks Nov 18, 2020
faeef8c
Remove 'unwrapNullable' parameter
GrabYourPitchforks Nov 18, 2020
bdb403a
Quick GetDefaultCtor helper
GrabYourPitchforks Nov 18, 2020
c173fda
Remove unmanaged Allocate / CreateInstance
GrabYourPitchforks Nov 18, 2020
5c52acd
Fix GetDefaultConstructor signature
GrabYourPitchforks Nov 19, 2020
a1c362c
Fix AV in allocator
GrabYourPitchforks Nov 19, 2020
3ce9c41
Fix indentation
GrabYourPitchforks Nov 19, 2020
904b25d
Fix COM instantiation
GrabYourPitchforks Nov 19, 2020
1f6434a
Remove COM allocator special-case
GrabYourPitchforks Nov 19, 2020
c1ecb07
Fix mono build break; quick code cleanup
GrabYourPitchforks Nov 19, 2020
a071262
Rename GetNewobjHelperFnPtr -> GetAllocatorFtn
GrabYourPitchforks Nov 19, 2020
dc417fd
Cleanup in the allocator routines
GrabYourPitchforks Nov 19, 2020
6904495
Fix linker failures, add asserts
GrabYourPitchforks Nov 19, 2020
05c35a8
Remove unwanted CreateInstanceDefaultCtor overload
GrabYourPitchforks Nov 19, 2020
3d8515a
Use WeakRef in ActivatorCache
GrabYourPitchforks Nov 19, 2020
5acef41
Reintroduce COM specialization
GrabYourPitchforks Nov 20, 2020
adfae42
Allow runtime to create unboxing stub on my behalf
GrabYourPitchforks Nov 20, 2020
49a8de1
Fix linker warning
GrabYourPitchforks Nov 20, 2020
98c7941
Update error conditions
GrabYourPitchforks Nov 20, 2020
0a443c2
Friendlier exception messages when activation fails
GrabYourPitchforks Nov 20, 2020
3b34ae6
Cleanup usings
GrabYourPitchforks Nov 20, 2020
6e1b7cd
Move friendly exception messages to common place
GrabYourPitchforks Nov 20, 2020
b588d5d
Fix TIE wrappers
GrabYourPitchforks Nov 20, 2020
55aa128
Update unit tests
GrabYourPitchforks Nov 20, 2020
0ef275c
Call CreateInstanceCheckThis for better exception handling
GrabYourPitchforks Nov 20, 2020
4c1f5dd
PR feedback + fix failing coreclr unit tests
GrabYourPitchforks Nov 20, 2020
97f5f60
Fix build breaks
GrabYourPitchforks Nov 20, 2020
867bdc0
Merge remote-tracking branch 'levib/fast_createinstance' into fast_cr…
GrabYourPitchforks Nov 20, 2020
2e44013
Fix strings.resx whitespace
GrabYourPitchforks Nov 20, 2020
9c267fd
Fix build breaks on mono & non-Windows
GrabYourPitchforks Nov 20, 2020
bead4c6
PR feedback
GrabYourPitchforks Nov 20, 2020
8f12dd8
Fix GC hole in AllocateComObject
GrabYourPitchforks Nov 21, 2020
3a2021d
Refactor GetActivationInfo
GrabYourPitchforks Nov 22, 2020
46ed0b8
Refactor RunClassConstructor
GrabYourPitchforks Nov 22, 2020
d3bc632
Update GetUninitializedObject to call the new APIs
GrabYourPitchforks Nov 22, 2020
3f362f2
Hook up ActivatorCache to new system
GrabYourPitchforks Nov 22, 2020
879937b
Fix typo causing build break
GrabYourPitchforks Nov 22, 2020
93bcedb
Change GetActivationInfo to QCALL
GrabYourPitchforks Nov 22, 2020
e04401f
Simplify some call sites
GrabYourPitchforks Nov 22, 2020
9887e9a
Merge remote-tracking branch 'origin/master' into fast_createinstance…
GrabYourPitchforks Nov 22, 2020
186fd83
Fix failing tests
GrabYourPitchforks Nov 22, 2020
4d3d9db
Fix bad assert
GrabYourPitchforks Nov 22, 2020
036cbf0
Add managed _AllocateComObject member to make FCall checks happier
GrabYourPitchforks Nov 22, 2020
882b63e
Avoid using GetEEFuncEntryPoint
GrabYourPitchforks Nov 22, 2020
0b0c8db
Fix build error in ecalllist.h
GrabYourPitchforks Nov 22, 2020
c50d287
Update COM invocation unit tests
GrabYourPitchforks Nov 22, 2020
e2235cc
Merge remote-tracking branch 'origin/master' into fast_createinstance…
GrabYourPitchforks Nov 22, 2020
cebbf48
Allow propagation of PNSE in RuntimeHandles
GrabYourPitchforks Nov 22, 2020
d373fe7
Merge remote-tracking branch 'origin/master' into fast_createinstance…
GrabYourPitchforks Nov 24, 2020
12b4578
Remove _AllocateComObject sentinel
GrabYourPitchforks Nov 24, 2020
a7fa617
PR feedback
GrabYourPitchforks Nov 24, 2020
3e9438c
PR feedback - simplify GetActivationInfo out params
GrabYourPitchforks Nov 25, 2020
9c20d84
Merge remote-tracking branch 'origin/master' into fast_createinstance…
GrabYourPitchforks Nov 25, 2020
4b38b6c
Add missing ifdef around _AllocateComInterop
GrabYourPitchforks Nov 25, 2020
9ed2b9b
Final inspection + code cleanup
GrabYourPitchforks Nov 25, 2020
0ebebd6
Fix compilation error on non-Windows
GrabYourPitchforks Nov 25, 2020
374cf34
Code cleanup & PR feedback
GrabYourPitchforks Nov 25, 2020
6b23817
Merge remote-tracking branch 'origin/master' into fast_createinstance…
GrabYourPitchforks Nov 25, 2020
11be754
More code cleanup
GrabYourPitchforks Nov 26, 2020
5d43a91
Rename ActivatorCache source file
GrabYourPitchforks Nov 26, 2020
0635399
Remove unnecessary asserts, underscores, indentation
GrabYourPitchforks Nov 26, 2020
9ac3c3f
Fix [DynamicallyAccessedMembers] annotations
GrabYourPitchforks Nov 26, 2020
a06ef73
Fix bad assert
GrabYourPitchforks Nov 26, 2020
1b1261f
Update src/coreclr/src/vm/reflectioninvocation.cpp
GrabYourPitchforks Nov 26, 2020
94f8614
PR feedback
GrabYourPitchforks Nov 26, 2020
c73e9fa
Update src/coreclr/src/vm/reflectioninvocation.cpp
jkotas Nov 26, 2020
359a588
Remove incorrect assert in ActivatorCache
GrabYourPitchforks Nov 26, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<UsingToolIbcOptimization>true</UsingToolIbcOptimization>
<UsingToolXliff>false</UsingToolXliff>
<!-- Upgrade compiler version to work around build failures: https://github.com/dotnet/runtime/issues/34417. -->
<MicrosoftNetCompilersToolsetVersion>3.7.0-2.20258.1</MicrosoftNetCompilersToolsetVersion>
<MicrosoftNetCompilersToolsetVersion>3.7.0-ci.final</MicrosoftNetCompilersToolsetVersion>
<!-- Blob storage container that has the "Latest" channel to publish to. -->
<ContainerName>dotnet</ContainerName>
<ChecksumContainerName>$(ContainerName)</ChecksumContainerName>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@
<_FullFrameworkReferenceAssemblyPaths>$(MSBuildThisFileDirectory)/Documentation</_FullFrameworkReferenceAssemblyPaths>
<SkipCommonResourcesIncludes>true</SkipCommonResourcesIncludes>
<DocumentationFile>$(OutputPath)$(MSBuildProjectName).xml</DocumentationFile>
<EnableAnalyzers>true</EnableAnalyzers>
<!-- Turning off analyzers for now because they fail when they see calli invocations -->
<EnableAnalyzers>false</EnableAnalyzers>
</PropertyGroup>

<!-- Platform specific properties -->
Expand Down Expand Up @@ -200,6 +201,7 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\MemberInfo.Internal.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\Metadata\AssemblyExtensions.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\MethodBase.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\ObjectFactory.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RtFieldInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeAssembly.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeConstructorInfo.cs" />
Expand All @@ -212,6 +214,7 @@
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeModule.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimeParameterInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\RuntimePropertyInfo.cs" />
<Compile Include="$(BclSourcesRoot)\System\Reflection\UninitializedObjectFactory.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Resources\ManifestBasedResourceGroveler.CoreCLR.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\CrossLoaderAllocatorHashHelpers.cs" />
<Compile Include="$(BclSourcesRoot)\System\Runtime\CompilerServices\DependentHandle.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics;
using System.Runtime.CompilerServices;
using Internal.Runtime.CompilerServices;

namespace System.Reflection
{
// Creates initialized instances of reference types or of value types.
// For reference types, calls the parameterless ctor.
// For value types, calls the parameterless ctor if it exists; otherwise
// return a boxed default(T). Must not be used with Nullable<T>.
internal unsafe sealed class ObjectFactory : UninitializedObjectFactory
{
private readonly void* _pfnCtor;
private readonly bool _isNonPublicCtor;

// Creates a factory from an existing parameterless ctor
internal ObjectFactory(RuntimeMethodHandleInternal hCtor)
: base(RuntimeMethodHandle.GetDeclaringType(hCtor))
{
_pfnCtor = (void*)RuntimeMethodHandle.GetFunctionPointer(hCtor);
Debug.Assert(_pfnCtor != null);

_isNonPublicCtor = (RuntimeMethodHandle.GetAttributes(hCtor) & MethodAttributes.MemberAccessMask) != MethodAttributes.Public;
}

private ObjectFactory(RuntimeType type)
: base(type)
{
Debug.Assert(_pMT->IsValueType);
_isNonPublicCtor = false; // default(T) is always "public"
}

// Creates a factory for "box(default(T))" around a value type
internal static ObjectFactory CreateFactoryForValueTypeDefaultOfT(RuntimeType type)
{
return new ObjectFactory(type);
}

public bool IsNonPublicCtor => _isNonPublicCtor;

public object CreateInstance()
{
object newObj = CreateUninitializedInstance();

if (!_pMT->IsValueType)
{
// Common case: we're creating a reference type
((delegate*<object, void>)_pfnCtor)(newObj);
}
else
{
// Less common case: we're creating a boxed value type
// If an explicit parameterless ctor exists, call it now.
if (_pfnCtor != null)
{
((delegate*<ref byte, void>)_pfnCtor)(ref newObj.GetRawData());
}
}

return newObj;
}
}

// Similar to ObjectFactory, but does not box value types 'T'.
internal unsafe sealed class ObjectFactory<T> : UninitializedObjectFactory
{
private readonly void* _pfnCtor;

internal ObjectFactory()
: base((RuntimeType)typeof(T))
{
RuntimeType type = (RuntimeType)typeof(T);

// It's ok if there's no default constructor on a value type.
// We'll return default(T). For reference types, the constructor
// must be present. In all cases, if a constructor is present, it
// must be public.

RuntimeMethodHandleInternal hCtor = RuntimeMethodHandleInternal.EmptyHandle;
if (_pMT->HasDefaultConstructor)
{
hCtor = RuntimeTypeHandle.GetDefaultConstructor(type);
Debug.Assert(!hCtor.IsNullHandle());
if ((RuntimeMethodHandle.GetAttributes(hCtor) & MethodAttributes.MemberAccessMask) != MethodAttributes.Public)
{
// parameterless ctor exists but is not public
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, type));
}

_pfnCtor = (void*)RuntimeMethodHandle.GetFunctionPointer(hCtor);
}
else
{
if (!_pMT->IsValueType)
{
// parameterless ctor missing on reference type
throw new MissingMethodException(SR.Format(SR.Arg_NoDefCTor, type));
}
}
}

public T CreateInstance()
{
if (typeof(T).IsValueType)
{
T value = default!;
if (_pfnCtor != null)
{
((delegate*<ref T, void>)_pfnCtor)(ref value);
}
return value;
}
else
{
object value = CreateUninitializedInstance();
Debug.Assert(_pfnCtor != null);
((delegate*<object, void>)_pfnCtor)(value!);
return Unsafe.As<object, T>(ref value);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace System.Reflection
{
// Creates zero-initialized instances of types.
// For reference types, equivalent of allocating memory without running ctor.
// For value types, equivalent of boxing default(T).
// Must not be used with Nullable<T>.
internal unsafe class UninitializedObjectFactory
{
protected readonly MethodTable* _pMT;
private readonly delegate*<MethodTable*, object> _pfnAllocate;
private readonly RuntimeType _type;

internal UninitializedObjectFactory(RuntimeType type)
{
Debug.Assert(type != null);
Debug.Assert(RuntimeHelpers.IsFastInstantiable(type));

_type = type;
_pMT = RuntimeTypeHandle.GetMethodTable(type);
_pfnAllocate = RuntimeHelpers.GetNewobjHelper(type);

Debug.Assert(_pMT != null);
Debug.Assert(!_pMT->IsNullable);
Debug.Assert(_pfnAllocate != null);
}

public object CreateUninitializedInstance()
{
// If a GC kicks in between the time we load the newobj
// helper address and the time we calli it, we don't want
// the Type object to be eligible for collection. To avoid
// this, we KeepAlive(this) - and the referenced Type -
// until we have an instance of the object. From that point
// onward, the object itself will keep the Type alive.

object newObj = _pfnAllocate(_pMT);
GC.KeepAlive(this);
return newObj;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.InteropServices;
using Internal.Runtime.CompilerServices;

Expand Down Expand Up @@ -145,6 +146,41 @@ public static int OffsetToStringData
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object AllocateUninitializedClone(object obj);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe delegate*<MethodTable*, object> GetNewobjHelper(RuntimeType type);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool IsFastInstantiable(RuntimeType type);

public static unsafe Func<object> GetUninitializedObjectFactory(Type type)
{
if (type is null)
{
throw new ArgumentNullException(nameof(type));
}

if (!(type.UnderlyingSystemType is RuntimeType rt))
{
throw new ArgumentException(SR.Arg_MustBeType, nameof(type));
}

type = null!; // just to make sure we don't use Type for the rest of the method

if (rt.IsPointer || rt.IsByRef || rt.IsByRefLike || !RuntimeHelpers.IsFastInstantiable(rt))
{
throw new ArgumentException(
paramName: nameof(type),
message: SR.NotSupported_Type);
}

// Compat with GetUninitializedObject: if incoming type T is really
// Nullable<U>, then this factory should return a boxed default(U)
// instead of a boxed default(Nullable<U>).

rt = (RuntimeType?)Nullable.GetUnderlyingType(rt) ?? rt;
return (Func<object>)new UninitializedObjectFactory(rt).CreateUninitializedInstance;
}

/// <returns>true if given type is reference type or value type that contains references</returns>
[Intrinsic]
public static bool IsReferenceOrContainsReferences<T>()
Expand Down Expand Up @@ -332,10 +368,15 @@ internal unsafe struct MethodTable
[FieldOffset(InterfaceMapOffset)]
public MethodTable** InterfaceMap;

// WFLAGS_HIGH_ENUM
// WFLAGS_HIGH_ENUM & WFLAGS_LOW_ENUM
private const uint enum_flag_Category_Mask = 0x000F0000;
private const uint enum_flag_Category_Nullable = 0x00050000;
private const uint enum_flag_Category_ValueType = 0x00040000;
private const uint enum_flag_Category_ValueType_Mask = 0x000C0000;
private const uint enum_flag_ContainsPointers = 0x01000000;
private const uint enum_flag_HasComponentSize = 0x80000000;
private const uint enum_flag_HasTypeEquivalence = 0x00004000;
private const uint enum_flag_HasDefaultCtor = 0x00000200;
private const uint enum_flag_HasTypeEquivalence = 0x00004000; // TODO: shouldn't this be 0x02000000?
// Types that require non-trivial interface cast have this bit set in the category
private const uint enum_flag_NonTrivialInterfaceCast = 0x00080000 // enum_flag_Category_Array
| 0x40000000 // enum_flag_ComObject
Expand Down Expand Up @@ -391,6 +432,30 @@ public bool NonTrivialInterfaceCast
}
}

public bool HasDefaultConstructor
{
get
{
return (Flags & enum_flag_HasDefaultCtor) != 0;
}
}

public bool IsNullable
{
get
{
return (Flags & enum_flag_Category_Mask) == enum_flag_Category_Nullable;
}
}

public bool IsValueType
{
get
{
return (Flags & enum_flag_Category_ValueType_Mask) == enum_flag_Category_ValueType;
}
}

public bool HasTypeEquivalence
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@ public ModuleHandle GetModuleHandle()
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern RuntimeMethodHandleInternal GetMethodAt(RuntimeType type, int slot);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static unsafe extern MethodTable* GetMethodTable(RuntimeType type);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern RuntimeMethodHandleInternal GetDefaultConstructor(RuntimeType type);

// This is managed wrapper for MethodTable::IntroducedMethodIterator
internal struct IntroducedMethodEnumerator
{
Expand Down
Loading