From d9aba33da15dc40c3fb03a161efc3339d2bec79b Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Wed, 6 Mar 2024 16:13:49 -0600 Subject: [PATCH 1/5] Improve perf\allocs of ActivatorUtilities.CreateInstance() --- .../src/ActivatorUtilities.cs | 197 ++++++++++++------ 1 file changed, 136 insertions(+), 61 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs index 8c8fcbd5f18b45..bab80000964520 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs @@ -28,9 +28,7 @@ public static class ActivatorUtilities // Support caching of constructor metadata for types in collectible assemblies. private static readonly Lazy> s_collectibleConstructorInfos = new(); -#endif -#if NET8_0_OR_GREATER // Maximum number of fixed arguments for ConstructorInvoker.Invoke(arg1, etc). private const int FixedArgumentThreshold = 4; #endif @@ -66,10 +64,16 @@ public static object CreateInstance( { constructors = GetOrAddConstructors(instanceType); } + + int maxArgs = GetMaxArgCount(); + object?[] values = new object?[maxArgs * 2]; + Span parameterValues = new(values, 0, maxArgs); #else constructors = CreateConstructorInfoExs(instanceType); + object?[]? parameterValues = null; #endif + ConstructorMatcher matcher = default; ConstructorInfoEx? constructor; IServiceProviderIsService? serviceProviderIsService = provider.GetService(); // if container supports using IServiceProviderIsService, we try to find the longest ctor that @@ -79,48 +83,71 @@ public static object CreateInstance( // instance if all parameters given to CreateInstance only match with a single ctor if (serviceProviderIsService != null) { - int bestLength = -1; - bool seenPreferred = false; - - ConstructorMatcher bestMatcher = default; - bool multipleBestLengthFound = false; - + // Handle the case where the attribute is used. for (int i = 0; i < constructors.Length; i++) { - constructor = constructors[i]; - ConstructorMatcher matcher = new(constructor); - bool isPreferred = constructor.IsPreferred; - int length = matcher.Match(parameters, serviceProviderIsService); - - if (isPreferred) + if (constructors[i].IsPreferred) { - if (seenPreferred) + for (int j = i + 1; j < constructors.Length; j++) { - ThrowMultipleCtorsMarkedWithAttributeException(); + if (constructors[j].IsPreferred) + { + ThrowMultipleCtorsMarkedWithAttributeException(); + } } - if (length == -1) +#if !NETCOREAPP + parameterValues = new object?[constructors[i].Parameters.Length]; +#endif + + matcher = new ConstructorMatcher(constructors[i], parameterValues); + if (matcher.Match(parameters, serviceProviderIsService) == -1) { ThrowMarkedCtorDoesNotTakeAllProvidedArguments(); } - seenPreferred = true; + return matcher.CreateInstance(provider); + } + } + + int bestLength = -1; + ConstructorMatcher bestMatcher = default; + bool multipleBestLengthFound = false; +#if NETCOREAPP + Span bestParameterValues = new(values, maxArgs, maxArgs); +#endif + + // Find the constructor with the most matches. + for (int i = 0; i < constructors.Length; i++) + { + constructor = constructors[i]; + +#if NETCOREAPP + parameterValues.Clear(); +#else + parameterValues = new object?[constructor.Parameters.Length]; +#endif + + matcher = new ConstructorMatcher(constructor, parameterValues); + int length = matcher.Match(parameters, serviceProviderIsService); + + Debug.Assert(!constructor.IsPreferred); + + if (bestLength < length) + { bestLength = length; - bestMatcher = matcher; +#if NETCOREAPP + parameterValues.CopyTo(bestParameterValues); +#else + object?[]? bestParameterValues = new object?[length]; + parameterValues.CopyTo(bestParameterValues, index: 0); +#endif + bestMatcher = new ConstructorMatcher(matcher.ConstructorInfo, bestParameterValues); multipleBestLengthFound = false; } - else if (!seenPreferred) + else if (bestLength == length) { - if (bestLength < length) - { - bestLength = length; - bestMatcher = matcher; - multipleBestLengthFound = false; - } - else if (bestLength == length) - { - multipleBestLengthFound = true; - } + multipleBestLengthFound = true; } } @@ -149,24 +176,25 @@ public static object CreateInstance( } } - FindApplicableConstructor(instanceType, argumentTypes, out ConstructorInfo constructorInfo, out int?[] parameterMap); + FindApplicableConstructor(instanceType, argumentTypes, constructors, out ConstructorInfo constructorInfo, out int?[] parameterMap); + constructor = FindConstructorEx(constructorInfo, constructors); + + matcher = new ConstructorMatcher(constructor, parameterValues); + matcher.MapParameters(parameterMap, parameters); + return matcher.CreateInstance(provider); - // Find the ConstructorInfoEx from the given constructorInfo. - constructor = null; - foreach (ConstructorInfoEx ctor in constructors) +#if NETCOREAPP + int GetMaxArgCount() { - if (ReferenceEquals(ctor.Info, constructorInfo)) + int max = 0; + for (int i = 0; i < constructors.Length; i++) { - constructor = ctor; - break; + max = int.Max(max, constructors[i].Parameters.Length); } - } - Debug.Assert(constructor != null); - - var constructorMatcher = new ConstructorMatcher(constructor); - constructorMatcher.MapParameters(parameterMap, parameters); - return constructorMatcher.CreateInstance(provider); + return max; + } +#endif } #if NETCOREAPP @@ -280,7 +308,7 @@ public static ObjectFactory private static void CreateFactoryInternal([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type instanceType, Type[] argumentTypes, out ParameterExpression provider, out ParameterExpression argumentArray, out Expression factoryExpressionBody) { - FindApplicableConstructor(instanceType, argumentTypes, out ConstructorInfo constructor, out int?[] parameterMap); + FindApplicableConstructor(instanceType, argumentTypes, constructors: null, out ConstructorInfo constructor, out int?[] parameterMap); provider = Expression.Parameter(typeof(IServiceProvider), "provider"); argumentArray = Expression.Parameter(typeof(object[]), "argumentArray"); @@ -401,10 +429,10 @@ private static ObjectFactory CreateFactoryReflection( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type instanceType, Type?[] argumentTypes) { - FindApplicableConstructor(instanceType, argumentTypes, out ConstructorInfo constructor, out int?[] parameterMap); + FindApplicableConstructor(instanceType, argumentTypes, constructors: null, out ConstructorInfo constructor, out int?[] parameterMap); Type declaringType = constructor.DeclaringType!; -#if NET8_0_OR_GREATER +#if NETCOREAPP ConstructorInvoker invoker = ConstructorInvoker.Create(constructor); ParameterInfo[] constructorParameters = constructor.GetParameters(); @@ -473,7 +501,7 @@ ObjectFactory InvokeCanonical() FactoryParameterContext[] parameters = GetFactoryParameterContext(); return (serviceProvider, arguments) => ReflectionFactoryCanonical(constructor, parameters, declaringType, serviceProvider, arguments); -#endif // NET8_0_OR_GREATER +#endif // NETCOREAPP FactoryParameterContext[] GetFactoryParameterContext() { @@ -518,13 +546,14 @@ public FactoryParameterContext(Type parameterType, bool hasDefaultValue, object? private static void FindApplicableConstructor( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type instanceType, Type?[] argumentTypes, + ConstructorInfoEx[]? constructors, out ConstructorInfo matchingConstructor, out int?[] matchingParameterMap) { ConstructorInfo? constructorInfo; int?[]? parameterMap; - if (!TryFindPreferredConstructor(instanceType, argumentTypes, out constructorInfo, out parameterMap) && + if (!TryFindPreferredConstructor(instanceType, argumentTypes, constructors, out constructorInfo, out parameterMap) && !TryFindMatchingConstructor(instanceType, argumentTypes, out constructorInfo, out parameterMap)) { throw new InvalidOperationException(SR.Format(SR.CtorNotLocated, instanceType)); @@ -534,6 +563,21 @@ private static void FindApplicableConstructor( matchingParameterMap = parameterMap; } + // Find the ConstructorInfoEx from the given constructorInfo. + private static ConstructorInfoEx FindConstructorEx(ConstructorInfo constructorInfo, ConstructorInfoEx[] constructorExs) + { + for (int i = 0; i < constructorExs.Length; i++) + { + if (ReferenceEquals(constructorExs[i].Info, constructorInfo)) + { + return constructorExs[i]; + } + } + + Debug.Assert(false); + return null!; + } + // Tries to find constructor based on provided argument types private static bool TryFindMatchingConstructor( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type instanceType, @@ -571,6 +615,7 @@ private static bool TryFindMatchingConstructor( private static bool TryFindPreferredConstructor( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type instanceType, Type?[] argumentTypes, + ConstructorInfoEx[]? constructors, [NotNullWhen(true)] out ConstructorInfo? matchingConstructor, [NotNullWhen(true)] out int?[]? parameterMap) { @@ -578,21 +623,33 @@ private static bool TryFindPreferredConstructor( matchingConstructor = null; parameterMap = null; - foreach (ConstructorInfo? constructor in instanceType.GetConstructors()) + if (constructors is null) { - if (constructor.IsDefined(typeof(ActivatorUtilitiesConstructorAttribute), false)) +#if NETCOREAPP + if (!s_constructorInfos.TryGetValue(instanceType, out constructors)) + { + constructors = GetOrAddConstructors(instanceType); + } +#else + constructors = CreateConstructorInfoExs(instanceType); +#endif + } + + foreach (ConstructorInfoEx constructor in constructors) + { + if (constructor.IsPreferred) { if (seenPreferred) { ThrowMultipleCtorsMarkedWithAttributeException(); } - if (!TryCreateParameterMap(constructor.GetParameters(), argumentTypes, out int?[] tempParameterMap)) + if (!TryCreateParameterMap(constructor.Info.GetParameters(), argumentTypes, out int?[] tempParameterMap)) { ThrowMarkedCtorDoesNotTakeAllProvidedArguments(); } - matchingConstructor = constructor; + matchingConstructor = constructor.Info; parameterMap = tempParameterMap; seenPreferred = true; } @@ -649,6 +706,17 @@ private sealed class ConstructorInfoEx public readonly ParameterInfo[] Parameters; public readonly bool IsPreferred; private readonly object?[]? _parameterKeys; +#if NETCOREAPP + public ConstructorInvoker? _invoker; + public ConstructorInvoker Invoker + { + get + { + _invoker ??= ConstructorInvoker.Create(Info); + return _invoker; + } + } +#endif public ConstructorInfoEx(ConstructorInfo constructor) { @@ -710,17 +778,24 @@ public bool IsService(IServiceProviderIsService serviceProviderIsService, int pa } } - private readonly struct ConstructorMatcher + private readonly ref struct ConstructorMatcher { private readonly ConstructorInfoEx _constructor; - private readonly object?[] _parameterValues; - public ConstructorMatcher(ConstructorInfoEx constructor) +#if NETCOREAPP + private readonly Span _parameterValues; + public ConstructorMatcher(ConstructorInfoEx constructor, Span parameterValues) +#else + private readonly object?[] _parameterValues; + public ConstructorMatcher(ConstructorInfoEx constructor, object?[] parameterValues) +#endif { _constructor = constructor; - _parameterValues = new object[constructor.Parameters.Length]; + _parameterValues = parameterValues; } + public ConstructorInfoEx ConstructorInfo => _constructor; + public int Match(object[] givenParameters, IServiceProviderIsService serviceProviderIsService) { for (int givenIndex = 0; givenIndex < givenParameters.Length; givenIndex++) @@ -790,7 +865,9 @@ public object CreateInstance(IServiceProvider provider) } } -#if NETFRAMEWORK || NETSTANDARD2_0 +#if NETCOREAPP + return _constructor.Invoker.Invoke(_parameterValues.Slice(0, _constructor.Parameters.Length)); +#else try { return _constructor.Info.Invoke(_parameterValues); @@ -801,8 +878,6 @@ public object CreateInstance(IServiceProvider provider) // The above line will always throw, but the compiler requires we throw explicitly. throw; } -#else - return _constructor.Info.Invoke(BindingFlags.DoNotWrapExceptions, binder: null, parameters: _parameterValues, culture: null); #endif } @@ -828,7 +903,7 @@ private static void ThrowMarkedCtorDoesNotTakeAllProvidedArguments() throw new InvalidOperationException(SR.Format(SR.MarkedCtorMissingArgumentTypes, nameof(ActivatorUtilitiesConstructorAttribute))); } -#if NET8_0_OR_GREATER // Use the faster ConstructorInvoker which also has alloc-free APIs when <= 4 parameters. +#if NETCOREAPP // Use the faster ConstructorInvoker which also has alloc-free APIs when <= 4 parameters. private static object ReflectionFactoryServiceOnlyFixed( ConstructorInvoker invoker, FactoryParameterContext[] parameters, @@ -1101,7 +1176,7 @@ private static object ReflectionFactoryCanonical( return constructor.Invoke(BindingFlags.DoNotWrapExceptions, binder: null, constructorArguments, culture: null); } -#endif // NET8_0_OR_GREATER +#endif #if NETCOREAPP internal static class ActivatorUtilitiesUpdateHandler From 33fa67c10aa83457aefedcb03213c34bc1d924e1 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Thu, 7 Mar 2024 14:12:41 -0600 Subject: [PATCH 2/5] Ready for review; use stack alloc if <=4 args --- .../src/ActivatorUtilities.cs | 79 +++++++++++++++---- 1 file changed, 62 insertions(+), 17 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs index bab80000964520..3e7d7b8a605247 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs @@ -9,6 +9,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; +using System.Runtime.InteropServices; using Microsoft.Extensions.Internal; #if NETCOREAPP @@ -65,12 +66,25 @@ public static object CreateInstance( constructors = GetOrAddConstructors(instanceType); } + // Attempt to use the stack allocated arg values if <= 4 ctor args. + Span values; + StackAllocatedObjects stackValues = default; int maxArgs = GetMaxArgCount(); - object?[] values = new object?[maxArgs * 2]; - Span parameterValues = new(values, 0, maxArgs); + if (maxArgs <= StackAllocatedObjects.MaxStackAllocArgCount / 2) + { + values = MemoryMarshal.CreateSpan(ref stackValues._args, maxArgs * 2); + } + else + { + values = new Span(new object?[maxArgs * 2], 0, maxArgs * 2); + } + + Span parameterValues = values.Slice(0, maxArgs); + Span bestParameterValues = values.Slice(maxArgs); #else constructors = CreateConstructorInfoExs(instanceType); object?[]? parameterValues = null; + object?[]? bestParameterValues = null; #endif ConstructorMatcher matcher = default; @@ -96,10 +110,7 @@ public static object CreateInstance( } } -#if !NETCOREAPP - parameterValues = new object?[constructors[i].Parameters.Length]; -#endif - + InitializeParameterValues(ref parameterValues, parameters.Length); matcher = new ConstructorMatcher(constructors[i], parameterValues); if (matcher.Match(parameters, serviceProviderIsService) == -1) { @@ -113,21 +124,13 @@ public static object CreateInstance( int bestLength = -1; ConstructorMatcher bestMatcher = default; bool multipleBestLengthFound = false; -#if NETCOREAPP - Span bestParameterValues = new(values, maxArgs, maxArgs); -#endif // Find the constructor with the most matches. for (int i = 0; i < constructors.Length; i++) { constructor = constructors[i]; -#if NETCOREAPP - parameterValues.Clear(); -#else - parameterValues = new object?[constructor.Parameters.Length]; -#endif - + InitializeParameterValues(ref parameterValues, parameters.Length); matcher = new ConstructorMatcher(constructor, parameterValues); int length = matcher.Match(parameters, serviceProviderIsService); @@ -139,8 +142,16 @@ public static object CreateInstance( #if NETCOREAPP parameterValues.CopyTo(bestParameterValues); #else - object?[]? bestParameterValues = new object?[length]; - parameterValues.CopyTo(bestParameterValues, index: 0); + if (i == constructors.Length - 1) + { + // Avoid the alloc for this case. + bestParameterValues = parameterValues; + } + else + { + bestParameterValues = new object?[length]; + parameterValues.CopyTo(bestParameterValues, 0); + } #endif bestMatcher = new ConstructorMatcher(matcher.ConstructorInfo, bestParameterValues); multipleBestLengthFound = false; @@ -179,6 +190,7 @@ public static object CreateInstance( FindApplicableConstructor(instanceType, argumentTypes, constructors, out ConstructorInfo constructorInfo, out int?[] parameterMap); constructor = FindConstructorEx(constructorInfo, constructors); + InitializeParameterValues(ref parameterValues, parameters.Length); matcher = new ConstructorMatcher(constructor, parameterValues); matcher.MapParameters(parameterMap, parameters); return matcher.CreateInstance(provider); @@ -195,6 +207,25 @@ int GetMaxArgCount() return max; } #endif + +#if NETCOREAPP + static void InitializeParameterValues(ref Span parameterValues, int _) + { + parameterValues.Clear(); + } +#else + static void InitializeParameterValues(ref object[] parameterValues, int length) + { + if (parameterValues is not null && parameterValues.Length == length) + { + Array.Clear(parameterValues, 0, length); + } + else + { + parameterValues = new object?[length]; + } + } +#endif } #if NETCOREAPP @@ -1191,6 +1222,20 @@ public static void ClearCache(Type[]? _) } } } + + [StructLayout(LayoutKind.Sequential)] + private ref struct StackAllocatedObjects + { + public const int MaxStackAllocArgCount = 8; + public object? _args; + private object? _arg2; + private object? _arg3; + private object? _arg4; + private object? _arg5; + private object? _arg6; + private object? _arg7; + private object? _arg8; + } #endif private static object? GetKeyedService(IServiceProvider provider, Type type, object? serviceKey) From 5d964cbf48ab5f298d8d8b64d0937a31dec8f304 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Fri, 8 Mar 2024 09:24:30 -0600 Subject: [PATCH 3/5] Use better naming between ctor args and incoming parameter values --- .../src/ActivatorUtilities.cs | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs index 3e7d7b8a605247..d5b913661345e2 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs @@ -79,12 +79,12 @@ public static object CreateInstance( values = new Span(new object?[maxArgs * 2], 0, maxArgs * 2); } - Span parameterValues = values.Slice(0, maxArgs); - Span bestParameterValues = values.Slice(maxArgs); + Span ctorArgs = values.Slice(0, maxArgs); + Span bestCtorArgs = values.Slice(maxArgs); #else constructors = CreateConstructorInfoExs(instanceType); - object?[]? parameterValues = null; - object?[]? bestParameterValues = null; + object?[]? ctorArgs = null; + object?[]? bestCtorArgs = null; #endif ConstructorMatcher matcher = default; @@ -100,7 +100,9 @@ public static object CreateInstance( // Handle the case where the attribute is used. for (int i = 0; i < constructors.Length; i++) { - if (constructors[i].IsPreferred) + constructor = constructors[i]; + + if (constructor.IsPreferred) { for (int j = i + 1; j < constructors.Length; j++) { @@ -110,8 +112,8 @@ public static object CreateInstance( } } - InitializeParameterValues(ref parameterValues, parameters.Length); - matcher = new ConstructorMatcher(constructors[i], parameterValues); + InitializeCtorArgValues(ref ctorArgs, constructor.Parameters.Length); + matcher = new ConstructorMatcher(constructor, ctorArgs); if (matcher.Match(parameters, serviceProviderIsService) == -1) { ThrowMarkedCtorDoesNotTakeAllProvidedArguments(); @@ -130,8 +132,8 @@ public static object CreateInstance( { constructor = constructors[i]; - InitializeParameterValues(ref parameterValues, parameters.Length); - matcher = new ConstructorMatcher(constructor, parameterValues); + InitializeCtorArgValues(ref ctorArgs, constructor.Parameters.Length); + matcher = new ConstructorMatcher(constructor, ctorArgs); int length = matcher.Match(parameters, serviceProviderIsService); Debug.Assert(!constructor.IsPreferred); @@ -140,20 +142,20 @@ public static object CreateInstance( { bestLength = length; #if NETCOREAPP - parameterValues.CopyTo(bestParameterValues); + ctorArgs.CopyTo(bestCtorArgs); #else if (i == constructors.Length - 1) { - // Avoid the alloc for this case. - bestParameterValues = parameterValues; + // We can prevent an alloc for the last case. + bestCtorArgs = ctorArgs; } else { - bestParameterValues = new object?[length]; - parameterValues.CopyTo(bestParameterValues, 0); + bestCtorArgs = new object?[length]; + ctorArgs.CopyTo(bestCtorArgs, 0); } #endif - bestMatcher = new ConstructorMatcher(matcher.ConstructorInfo, bestParameterValues); + bestMatcher = new ConstructorMatcher(matcher.ConstructorInfo, bestCtorArgs); multipleBestLengthFound = false; } else if (bestLength == length) @@ -190,8 +192,8 @@ public static object CreateInstance( FindApplicableConstructor(instanceType, argumentTypes, constructors, out ConstructorInfo constructorInfo, out int?[] parameterMap); constructor = FindConstructorEx(constructorInfo, constructors); - InitializeParameterValues(ref parameterValues, parameters.Length); - matcher = new ConstructorMatcher(constructor, parameterValues); + InitializeCtorArgValues(ref ctorArgs, constructor.Parameters.Length); + matcher = new ConstructorMatcher(constructor, ctorArgs); matcher.MapParameters(parameterMap, parameters); return matcher.CreateInstance(provider); @@ -209,20 +211,20 @@ int GetMaxArgCount() #endif #if NETCOREAPP - static void InitializeParameterValues(ref Span parameterValues, int _) + static void InitializeCtorArgValues(ref Span ctorArgs, int _) { - parameterValues.Clear(); + ctorArgs.Clear(); } #else - static void InitializeParameterValues(ref object[] parameterValues, int length) + static void InitializeCtorArgValues(ref object[] ctorArgs, int length) { - if (parameterValues is not null && parameterValues.Length == length) + if (ctorArgs is not null && ctorArgs.Length == length) { - Array.Clear(parameterValues, 0, length); + Array.Clear(ctorArgs, 0, length); } else { - parameterValues = new object?[length]; + ctorArgs = new object?[length]; } } #endif From 4d86b7be63909c8c86d8e96cac7a649dece66395 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Mon, 11 Mar 2024 10:57:29 -0500 Subject: [PATCH 4/5] Use [InlineArray] --- .../src/ActivatorUtilities.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs index d5b913661345e2..240c03f0014c0f 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs @@ -72,7 +72,7 @@ public static object CreateInstance( int maxArgs = GetMaxArgCount(); if (maxArgs <= StackAllocatedObjects.MaxStackAllocArgCount / 2) { - values = MemoryMarshal.CreateSpan(ref stackValues._args, maxArgs * 2); + values = MemoryMarshal.CreateSpan(ref stackValues._args._arg0, maxArgs * 2); } else { @@ -1228,15 +1228,14 @@ public static void ClearCache(Type[]? _) [StructLayout(LayoutKind.Sequential)] private ref struct StackAllocatedObjects { - public const int MaxStackAllocArgCount = 8; - public object? _args; - private object? _arg2; - private object? _arg3; - private object? _arg4; - private object? _arg5; - private object? _arg6; - private object? _arg7; - private object? _arg8; + internal const int MaxStackAllocArgCount = 8; + internal StackAllocatedObjectValues _args; + + [InlineArray(MaxStackAllocArgCount)] + internal struct StackAllocatedObjectValues + { + internal object? _arg0; + } } #endif From 99b49ca6b824e52e28215de57d1fdaef6a44ee90 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Wed, 13 Mar 2024 15:26:43 -0500 Subject: [PATCH 5/5] Combine #if sections --- .../src/ActivatorUtilities.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs index 240c03f0014c0f..a7d8701061e5a1 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ActivatorUtilities.cs @@ -208,9 +208,7 @@ int GetMaxArgCount() return max; } -#endif -#if NETCOREAPP static void InitializeCtorArgValues(ref Span ctorArgs, int _) { ctorArgs.Clear();