From f25cef14ccf598c13382653b6532ed15d6a403d1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 9 Jul 2019 23:37:53 +0200 Subject: [PATCH] Reduce JIT and type load overhead in WinForms startup (dotnet/corefx#39315) https://github.com/dotnet/corefx/pull/37853#discussion_r300974699 The existing code was trading startup perf for throughput in a codepath that is unlikely to be executed commonly. The common scenario is that the `UserPreferencesChanging` event is not fired for the entire lifetime of the app (I left an app listening for this event running for an hour of normal work and never once was the event fired. I then changed system colors to force it). To set up the high performance path, we have to load two unique `Func` instantiations and JIT a generic method. It's likely the throughput optimization won't pay for itself in terms of CPU cycles even if someone does change quite a few settings. Commit migrated from https://github.com/dotnet/corefx/commit/3c37204d825c567ead19d8b2d9513efca25dd7c8 --- .../Common/src/System/Drawing/KnownColorTable.cs | 34 +++++----------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/src/libraries/Common/src/System/Drawing/KnownColorTable.cs b/src/libraries/Common/src/System/Drawing/KnownColorTable.cs index b5a3754..6a1d5ea 100644 --- a/src/libraries/Common/src/System/Drawing/KnownColorTable.cs +++ b/src/libraries/Common/src/System/Drawing/KnownColorTable.cs @@ -9,7 +9,7 @@ namespace System.Drawing { static internal class KnownColorTable { - private static Func s_categoryGetter; + private static MethodInfo s_categoryGetter; private static int[] s_colorTable; private static string[] s_colorNameTable; @@ -60,13 +60,13 @@ namespace System.Drawing // SystemColors. In order to avoid a static dependency on SystemEvents since it is not available on all platforms // and as such we don't want to bring it into the shared framework. We use the desktop identity for SystemEvents // since it is stable and will remain stable for compatibility whereas the core identity could change. - Type systemEventsType = Type.GetType("Microsoft.Win32.SystemEvents, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", throwOnError: false); + Type systemEventsType = Type.GetType("Microsoft.Win32.SystemEvents, Microsoft.Win32.SystemEvents", throwOnError: false); EventInfo upEventInfo = systemEventsType?.GetEvent("UserPreferenceChanging", BindingFlags.Public | BindingFlags.Static); if (upEventInfo != null) { // Delegate TargetType - Type userPrefChangingDelegateType = Type.GetType("Microsoft.Win32.UserPreferenceChangingEventHandler, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", throwOnError: false); + Type userPrefChangingDelegateType = Type.GetType("Microsoft.Win32.UserPreferenceChangingEventHandler, Microsoft.Win32.SystemEvents", throwOnError: false); Debug.Assert(userPrefChangingDelegateType != null); if (userPrefChangingDelegateType != null) @@ -83,18 +83,9 @@ namespace System.Drawing } // Retrieving getter of the category property of the UserPreferenceChangingEventArgs. - Type argsType = Type.GetType("Microsoft.Win32.UserPreferenceChangingEventArgs, System, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089", throwOnError: false); - PropertyInfo categoryProperty = argsType?.GetProperty("Category", BindingFlags.Instance | BindingFlags.Public); - MethodInfo categoryGetter = categoryProperty?.GetGetMethod(); - - Debug.Assert(categoryGetter != null); - if (categoryGetter != null) - { - // Creating a Delegate pointing to the getter method. - s_categoryGetter = (Func)typeof(KnownColorTable).GetMethod(nameof(CreateGetter), BindingFlags.NonPublic | BindingFlags.Static) - .MakeGenericMethod(argsType, categoryGetter.ReturnType) - .Invoke(null, new object[] { categoryGetter }); - } + Type argsType = Type.GetType("Microsoft.Win32.UserPreferenceChangingEventArgs, Microsoft.Win32.SystemEvents", throwOnError: false); + s_categoryGetter = argsType?.GetMethod("get_Category", BindingFlags.Instance | BindingFlags.Public); + Debug.Assert(s_categoryGetter != null); } } @@ -246,15 +237,6 @@ namespace System.Drawing s_colorTable = values; } - // Generic method that we specialize at runtime once we've loaded the UserPreferenceChangingEventArgs type. - // It permits creating an unbound delegate so that we can avoid reflection after the initial - // lightup code completes. - private static Func CreateGetter(MethodInfo method) where TInstance : EventArgs where TProperty : Enum - { - Func typedDelegate = (Func)Delegate.CreateDelegate(typeof(Func), null, method); - return (EventArgs instance) => (int)(object)typedDelegate((TInstance)instance); - } - private static void EnsureColorNameTable() { // no need to lock... worse case is a double create of the table... @@ -486,8 +468,8 @@ namespace System.Drawing private static void OnUserPreferenceChanging(object sender, EventArgs args) { Debug.Assert(s_categoryGetter != null); - // Access UserPreferenceChangingEventArgs.Category value through cached delegate. - if (s_colorTable != null && s_categoryGetter != null && s_categoryGetter(args) == 2) // UserPreferenceCategory.Color = 2 + // Access UserPreferenceChangingEventArgs.Category value through cached MethodInfo. + if (s_colorTable != null && s_categoryGetter != null && (int)s_categoryGetter.Invoke(args, Array.Empty()) == 2) // UserPreferenceCategory.Color = 2 { UpdateSystemColors(s_colorTable); } -- 2.7.4