Reduce RuntimeType.MakeGenericType overheads (#45137)
authorStephen Toub <stoub@microsoft.com>
Tue, 24 Nov 2020 12:03:10 +0000 (07:03 -0500)
committerGitHub <noreply@github.com>
Tue, 24 Nov 2020 12:03:10 +0000 (07:03 -0500)
- Avoid an extra GetGenericArguments() call for all arities.
- Special-case a Type[] with just one type.  In looking at all calls to MakeGenericType when starting up a basic ASP.NET MVC app, 70% were for a single generic argument (the rest were for two).

src/coreclr/src/System.Private.CoreLib/src/System/RuntimeHandles.cs
src/coreclr/src/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs

index 30c9df8..cd9dfa1 100644 (file)
@@ -522,6 +522,17 @@ namespace System
         [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
         private static extern void Instantiate(QCallTypeHandle handle, IntPtr* pInst, int numGenericArgs, ObjectHandleOnStack type);
 
+        internal RuntimeType Instantiate(RuntimeType inst)
+        {
+            IntPtr ptr = inst.TypeHandle.Value;
+
+            RuntimeType? type = null;
+            RuntimeTypeHandle nativeHandle = GetNativeHandle();
+            Instantiate(new QCallTypeHandle(ref nativeHandle), &ptr, 1, ObjectHandleOnStack.Create(ref type));
+            GC.KeepAlive(inst);
+            return type!;
+        }
+
         internal RuntimeType Instantiate(Type[]? inst)
         {
             IntPtr[]? instHandles = CopyRuntimeTypeHandles(inst, out int instCount);
index 0c1ba94..1b968d2 100644 (file)
@@ -3238,15 +3238,29 @@ namespace System
             if (instantiation == null)
                 throw new ArgumentNullException(nameof(instantiation));
 
-            RuntimeType[] instantiationRuntimeType = new RuntimeType[instantiation.Length];
-
             if (!IsGenericTypeDefinition)
-                throw new InvalidOperationException(
-                    SR.Format(SR.Arg_NotGenericTypeDefinition, this));
+                throw new InvalidOperationException(SR.Format(SR.Arg_NotGenericTypeDefinition, this));
 
-            if (GetGenericArguments().Length != instantiation.Length)
+            RuntimeType[] genericParameters = GetGenericArgumentsInternal();
+            if (genericParameters.Length != instantiation.Length)
                 throw new ArgumentException(SR.Argument_GenericArgsCount, nameof(instantiation));
 
+            if (instantiation.Length == 1 && instantiation[0] is RuntimeType rt)
+            {
+                ThrowIfTypeNeverValidGenericArgument(rt);
+                try
+                {
+                    return new RuntimeTypeHandle(this).Instantiate(rt);
+                }
+                catch (TypeLoadException e)
+                {
+                    ValidateGenericArguments(this, new[] { rt }, e);
+                    throw;
+                }
+            }
+
+            RuntimeType[] instantiationRuntimeType = new RuntimeType[instantiation.Length];
+
             bool foundSigType = false;
             bool foundNonRuntimeType = false;
             for (int i = 0; i < instantiation.Length; i++)
@@ -3277,8 +3291,6 @@ namespace System
                 return System.Reflection.Emit.TypeBuilderInstantiation.MakeGenericType(this, (Type[])(instantiation.Clone()));
             }
 
-            RuntimeType[] genericParameters = GetGenericArgumentsInternal();
-
             SanityCheckGenericArguments(instantiationRuntimeType, genericParameters);
 
             Type ret;