Do less work building Delegate.GetMethodInfo lookup (#86930)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Wed, 31 May 2023 05:40:57 +0000 (14:40 +0900)
committerGitHub <noreply@github.com>
Wed, 31 May 2023 05:40:57 +0000 (22:40 -0700)
This code is very problematic for startup. We're spending 6.6% of startup samples of the Stage2 app in `ComputeLdftnReverseLookup_InvokeMap`. This code supports `Delegate.GetMethodInfo` and builds a reverse lookup table to lookup code metadata from code address. The data structure we have in the image is optimized for lookups in the other direction. 2.2% of the total time is spent trying to decode the entrypoint as an unboxing stub (the requires looking up unwinding info for the method). Shortcut this and do it only for valuetypes.

Medium term we'd want to do something about this. In the end this is the same problem as https://github.com/dotnet/runtime/pull/79921#discussion_r1056547078. Except this time we need to be able to lookup this information both ways (from metadata to function pointer and from function pointer to metadata). One thing I'm thinking about is whether we could store the hashcode of the owning type in the unwinding information of the method to give us something to go off.

src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs

index 1da0458..8f1a187 100644 (file)
@@ -743,7 +743,7 @@ namespace Internal.Reflection.Execution
                     continue;
 
                 entryParser.SkipInteger(); // entryMethodHandleOrNameAndSigRaw
-                entryParser.SkipInteger(); // entryDeclaringTypeRaw
+                RuntimeTypeHandle declaringTypeHandle = externalReferences.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned());
 
                 IntPtr entryMethodEntrypoint = externalReferences.GetFunctionPointerFromIndex(entryParser.GetUnsigned());
                 functionPointers.Add(new FunctionPointerOffsetPair(entryMethodEntrypoint, parserOffset));
@@ -752,17 +752,21 @@ namespace Internal.Reflection.Execution
                 // stack trace resolution - the reverse LdFtn lookup internally used by the reflection
                 // method resolution will work off an IP address on the stack which is an address
                 // within the actual method, not the stub.
-                IntPtr targetAddress = RuntimeAugments.GetCodeTarget(entryMethodEntrypoint);
-                if (targetAddress != IntPtr.Zero && targetAddress != entryMethodEntrypoint)
+                if (RuntimeAugments.IsValueType(declaringTypeHandle))
                 {
-                    functionPointers.Add(new FunctionPointerOffsetPair(targetAddress, parserOffset));
-                }
-                IntPtr targetAddress2;
-                if (TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(entryMethodEntrypoint, out targetAddress2) &&
-                    targetAddress2 != entryMethodEntrypoint &&
-                    targetAddress2 != targetAddress)
-                {
-                    functionPointers.Add(new FunctionPointerOffsetPair(targetAddress2, parserOffset));
+                    IntPtr targetAddress = RuntimeAugments.GetCodeTarget(entryMethodEntrypoint);
+                    if (targetAddress != IntPtr.Zero && targetAddress != entryMethodEntrypoint)
+                    {
+                        functionPointers.Add(new FunctionPointerOffsetPair(targetAddress, parserOffset));
+                    }
+
+                    IntPtr targetAddress2;
+                    if (TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(entryMethodEntrypoint, out targetAddress2) &&
+                        targetAddress2 != entryMethodEntrypoint &&
+                        targetAddress2 != targetAddress)
+                    {
+                        functionPointers.Add(new FunctionPointerOffsetPair(targetAddress2, parserOffset));
+                    }
                 }
             }