Skip to content

Commit

Permalink
Avoid a few array allocations with DefaultBinder (#93115)
Browse files Browse the repository at this point in the history
* Avoid a few array allocations with DefaultBinder

In places where we're not handing out a `T[]` and thus actually need a `T[]`, we can instead just get a span from the `ListBuilder<T>` being used.

* Fix mono and nativeaot call sites, and share identical ListBuilder between coreclr/mono

* Dedup ListBuilder in nativeaot
  • Loading branch information
stephentoub committed Oct 11, 2023
1 parent 7f63b6b commit 565ee96
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 256 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal static IList<CustomAttributeData> GetCustomAttributesInternal(RuntimeTy
Debug.Assert(target is not null);

IList<CustomAttributeData> cad = GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken);
RuntimeType.ListBuilder<Attribute> pcas = default;
ListBuilder<Attribute> pcas = default;
PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), ref pcas);
return pcas.Count > 0 ? GetCombinedList(cad, ref pcas) : cad;
}
Expand All @@ -28,7 +28,7 @@ internal static IList<CustomAttributeData> GetCustomAttributesInternal(RuntimeFi
Debug.Assert(target is not null);

IList<CustomAttributeData> cad = GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken);
RuntimeType.ListBuilder<Attribute> pcas = default;
ListBuilder<Attribute> pcas = default;
PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), ref pcas);
return pcas.Count > 0 ? GetCombinedList(cad, ref pcas) : cad;
}
Expand All @@ -38,7 +38,7 @@ internal static IList<CustomAttributeData> GetCustomAttributesInternal(RuntimeMe
Debug.Assert(target is not null);

IList<CustomAttributeData> cad = GetCustomAttributes(target.GetRuntimeModule(), target.MetadataToken);
RuntimeType.ListBuilder<Attribute> pcas = default;
ListBuilder<Attribute> pcas = default;
PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), ref pcas);
return pcas.Count > 0 ? GetCombinedList(cad, ref pcas) : cad;
}
Expand Down Expand Up @@ -87,13 +87,13 @@ internal static IList<CustomAttributeData> GetCustomAttributesInternal(RuntimePa
{
Debug.Assert(target is not null);

RuntimeType.ListBuilder<Attribute> pcas = default;
ListBuilder<Attribute> pcas = default;
IList<CustomAttributeData> cad = GetCustomAttributes(target.GetRuntimeModule()!, target.MetadataToken);
PseudoCustomAttribute.GetCustomAttributes(target, (RuntimeType)typeof(object), ref pcas);
return pcas.Count > 0 ? GetCombinedList(cad, ref pcas) : cad;
}

private static ReadOnlyCollection<CustomAttributeData> GetCombinedList(IList<CustomAttributeData> customAttributes, ref RuntimeType.ListBuilder<Attribute> pseudoAttributes)
private static ReadOnlyCollection<CustomAttributeData> GetCombinedList(IList<CustomAttributeData> customAttributes, ref ListBuilder<Attribute> pseudoAttributes)
{
Debug.Assert(pseudoAttributes.Count != 0);

Expand Down Expand Up @@ -922,7 +922,7 @@ internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caTyp
if (type.IsGenericType && !type.IsGenericTypeDefinition)
type = (type.GetGenericTypeDefinition() as RuntimeType)!;

RuntimeType.ListBuilder<Attribute> pcas = default;
ListBuilder<Attribute> pcas = default;
PseudoCustomAttribute.GetCustomAttributes(type, caType, ref pcas);

// if we are asked to go up the hierarchy chain we have to do it now and regardless of the
Expand All @@ -935,7 +935,7 @@ internal static object[] GetCustomAttributes(RuntimeType type, RuntimeType caTyp
return attributes;
}

RuntimeType.ListBuilder<object> result = default;
ListBuilder<object> result = default;
bool mustBeInheritable = false;

for (int i = 0; i < pcas.Count; i++)
Expand Down Expand Up @@ -964,7 +964,7 @@ internal static object[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeTy
if (method.IsGenericMethod && !method.IsGenericMethodDefinition)
method = (method.GetGenericMethodDefinition() as RuntimeMethodInfo)!;

RuntimeType.ListBuilder<Attribute> pcas = default;
ListBuilder<Attribute> pcas = default;
PseudoCustomAttribute.GetCustomAttributes(method, caType, ref pcas);

// if we are asked to go up the hierarchy chain we have to do it now and regardless of the
Expand All @@ -977,7 +977,7 @@ internal static object[] GetCustomAttributes(RuntimeMethodInfo method, RuntimeTy
return attributes;
}

RuntimeType.ListBuilder<object> result = default;
ListBuilder<object> result = default;
bool mustBeInheritable = false;

for (int i = 0; i < pcas.Count; i++)
Expand Down Expand Up @@ -1033,7 +1033,7 @@ internal static object[] GetCustomAttributes(RuntimeFieldInfo field, RuntimeType
Debug.Assert(field is not null);
Debug.Assert(caType is not null);

RuntimeType.ListBuilder<Attribute> pcas = default;
ListBuilder<Attribute> pcas = default;
PseudoCustomAttribute.GetCustomAttributes(field, caType, ref pcas);
object[] attributes = GetCustomAttributes(field.GetRuntimeModule(), field.MetadataToken, pcas.Count, caType);
if (pcas.Count > 0) pcas.CopyTo(attributes, attributes.Length - pcas.Count);
Expand All @@ -1045,7 +1045,7 @@ internal static object[] GetCustomAttributes(RuntimeParameterInfo parameter, Run
Debug.Assert(parameter is not null);
Debug.Assert(caType is not null);

RuntimeType.ListBuilder<Attribute> pcas = default;
ListBuilder<Attribute> pcas = default;
PseudoCustomAttribute.GetCustomAttributes(parameter, caType, ref pcas);
object[] attributes = GetCustomAttributes(parameter.GetRuntimeModule()!, parameter.MetadataToken, pcas.Count, caType);
if (pcas.Count > 0) pcas.CopyTo(attributes, attributes.Length - pcas.Count);
Expand Down Expand Up @@ -1101,7 +1101,7 @@ internal static bool IsAttributeDefined(RuntimeModule decoratedModule, int decor
{
Debug.Assert(attributeCtorToken == 0);

RuntimeType.ListBuilder<object> derivedAttributes = default;
ListBuilder<object> derivedAttributes = default;

for (int i = 0; i < attributeTokens.Length; i++)
{
Expand Down Expand Up @@ -1139,7 +1139,7 @@ internal static bool IsAttributeDefined(RuntimeModule decoratedModule, int decor
private static object[] GetCustomAttributes(
RuntimeModule decoratedModule, int decoratedMetadataToken, int pcaCount, RuntimeType attributeFilterType)
{
RuntimeType.ListBuilder<object> attributes = default;
ListBuilder<object> attributes = default;

AddCustomAttributes(ref attributes, decoratedModule, decoratedMetadataToken, attributeFilterType, false, default);

Expand All @@ -1156,11 +1156,11 @@ internal static bool IsAttributeDefined(RuntimeModule decoratedModule, int decor
"attribute instantiation which is present in the code linker has analyzed." +
"As such the reflection usage in this method will never fail as those methods/fields will be present.")]
private static void AddCustomAttributes(
ref RuntimeType.ListBuilder<object> attributes,
ref ListBuilder<object> attributes,
RuntimeModule decoratedModule, int decoratedMetadataToken,
RuntimeType? attributeFilterType, bool mustBeInheritable,
// The derivedAttributes list must be passed by value so that it is not modified with the discovered attributes
RuntimeType.ListBuilder<object> derivedAttributes)
ListBuilder<object> derivedAttributes)
{
CustomAttributeRecord[] car = RuntimeCustomAttributeData.GetCustomAttributeRecords(decoratedModule, decoratedMetadataToken);

Expand Down Expand Up @@ -1295,7 +1295,7 @@ internal static bool IsAttributeDefined(RuntimeModule decoratedModule, int decor
MetadataToken decoratedToken,
RuntimeType attributeFilterType,
bool mustBeInheritable,
ref RuntimeType.ListBuilder<object> derivedAttributes,
ref ListBuilder<object> derivedAttributes,
out RuntimeType attributeType,
out IRuntimeMethodInfo? ctorWithParameters,
out bool isVarArg)
Expand Down Expand Up @@ -1409,7 +1409,7 @@ private static bool MatchesTypeFilter(RuntimeType attributeType, RuntimeType att

#region Private Static Methods
private static bool AttributeUsageCheck(
RuntimeType attributeType, bool mustBeInheritable, ref RuntimeType.ListBuilder<object> derivedAttributes)
RuntimeType attributeType, bool mustBeInheritable, ref ListBuilder<object> derivedAttributes)
{
AttributeUsageAttribute? attributeUsageAttribute = null;

Expand Down Expand Up @@ -1595,7 +1595,7 @@ private static void VerifyPseudoCustomAttribute(RuntimeType pca)
#endregion

#region Internal Static
internal static void GetCustomAttributes(RuntimeType type, RuntimeType caType, ref RuntimeType.ListBuilder<Attribute> pcas)
internal static void GetCustomAttributes(RuntimeType type, RuntimeType caType, ref ListBuilder<Attribute> pcas)
{
Debug.Assert(type is not null);
Debug.Assert(caType is not null);
Expand Down Expand Up @@ -1639,7 +1639,7 @@ internal static bool IsDefined(RuntimeType type, RuntimeType? caType)
return false;
}

internal static void GetCustomAttributes(RuntimeMethodInfo method, RuntimeType caType, ref RuntimeType.ListBuilder<Attribute> pcas)
internal static void GetCustomAttributes(RuntimeMethodInfo method, RuntimeType caType, ref ListBuilder<Attribute> pcas)
{
Debug.Assert(method is not null);
Debug.Assert(caType is not null);
Expand Down Expand Up @@ -1679,7 +1679,7 @@ internal static bool IsDefined(RuntimeMethodInfo method, RuntimeType? caType)
return false;
}

internal static void GetCustomAttributes(RuntimeParameterInfo parameter, RuntimeType caType, ref RuntimeType.ListBuilder<Attribute> pcas)
internal static void GetCustomAttributes(RuntimeParameterInfo parameter, RuntimeType caType, ref ListBuilder<Attribute> pcas)
{
Debug.Assert(parameter is not null);
Debug.Assert(caType is not null);
Expand Down Expand Up @@ -1735,7 +1735,7 @@ internal static bool IsDefined(RuntimeParameterInfo parameter, RuntimeType? caTy
return false;
}

internal static void GetCustomAttributes(RuntimeFieldInfo field, RuntimeType caType, ref RuntimeType.ListBuilder<Attribute> pcas)
internal static void GetCustomAttributes(RuntimeFieldInfo field, RuntimeType caType, ref ListBuilder<Attribute> pcas)
{
Debug.Assert(field is not null);
Debug.Assert(caType is not null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,87 +53,6 @@ internal enum MemberListType
HandleToInfo
}

// Helper to build lists of MemberInfos. Special cased to avoid allocations for lists of one element.
internal struct ListBuilder<T> where T : class
{
private T[]? _items;
private T _item;
private int _count;
private int _capacity;

public ListBuilder(int capacity)
{
_items = null;
_item = null!;
_count = 0;
_capacity = capacity;
}

public T this[int index]
{
get
{
Debug.Assert(index < Count);
return (_items != null) ? _items[index] : _item;
}
}

public T[] ToArray()
{
if (_count == 0)
return Array.Empty<T>();
if (_count == 1)
return new T[1] { _item };

Array.Resize(ref _items, _count);
_capacity = _count;
return _items!;
}

public void CopyTo(object[] array, int index)
{
if (_count == 0)
return;

if (_count == 1)
{
array[index] = _item;
return;
}

Array.Copy(_items!, 0, array, index, _count);
}

public int Count => _count;

public void Add(T item)
{
if (_count == 0)
{
_item = item;
}
else
{
if (_count == 1)
{
if (_capacity < 2)
_capacity = 4;
_items = new T[_capacity];
_items[0] = _item;
}
else if (_capacity == _count)
{
int newCapacity = 2 * _capacity;
Array.Resize(ref _items, newCapacity);
_capacity = newCapacity;
}

_items![_count] = item;
}
_count++;
}
}

internal sealed class RuntimeTypeCache
{
private const int MAXNAMELEN = 1024;
Expand Down Expand Up @@ -2826,7 +2745,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
}

// All the methods have the exact same name and sig so return the most derived one.
return System.DefaultBinder.FindMostDerivedNewSlotMeth(candidates.ToArray(), candidates.Count) as MethodInfo;
return System.DefaultBinder.FindMostDerivedNewSlotMeth(candidates.AsSpan());
}
}

Expand Down Expand Up @@ -2855,7 +2774,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
}

if ((bindingAttr & BindingFlags.ExactBinding) != 0)
return System.DefaultBinder.ExactBinding(candidates.ToArray(), types) as ConstructorInfo;
return System.DefaultBinder.ExactBinding(candidates.AsSpan(), types);

binder ??= DefaultBinder;
return binder.SelectMethod(bindingAttr, candidates.ToArray(), types, modifiers) as ConstructorInfo;
Expand Down Expand Up @@ -2893,7 +2812,7 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
}

if ((bindingAttr & BindingFlags.ExactBinding) != 0)
return System.DefaultBinder.ExactPropertyBinding(candidates.ToArray(), returnType, types);
return System.DefaultBinder.ExactPropertyBinding(candidates.AsSpan(), returnType, types);

binder ??= DefaultBinder;
return binder.SelectProperty(bindingAttr, candidates.ToArray(), returnType, types, modifiers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,6 @@
<Compile Include="System\Reflection\Runtime\General\Helpers.cs" />
<Compile Include="System\Reflection\Runtime\General\IRuntimeMemberInfoWithNoMetadataDefinition.cs" />
<Compile Include="System\Reflection\Runtime\General\LegacyCustomAttributeApis.cs" />
<Compile Include="System\Reflection\Runtime\General\ListBuilder.cs" />
<Compile Include="System\Reflection\Runtime\General\MetadataReaderExtensions.cs" />
<Compile Include="System\Reflection\Runtime\General\MetadataReaderExtensions.NativeFormat.cs" />
<Compile Include="System\Reflection\Runtime\General\NamespaceChain.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ protected sealed override ConstructorInfo GetConstructorImpl(BindingFlags bindin
}

if ((bindingAttr & BindingFlags.ExactBinding) != 0)
return System.DefaultBinder.ExactBinding(candidates.ToArray(), types) as ConstructorInfo;
return System.DefaultBinder.ExactBinding(candidates.AsSpan(), types);

binder ??= DefaultBinder;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,7 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\InvokeUtils.cs" Condition="'$(FeatureNativeAot)' != 'true'" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\IReflect.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\IReflectableType.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ListBuilder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\LocalVariableInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\ManifestResourceInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\MemberFilter.cs" />
Expand Down Expand Up @@ -2719,4 +2720,4 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Numerics\IUnaryPlusOperators.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Numerics\IUnsignedNumber.cs" />
</ItemGroup>
</Project>
</Project>
Loading

0 comments on commit 565ee96

Please sign in to comment.