From 702ea5cea004e936c9ca4d3d104040abc04bd9a8 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 3 Jan 2020 09:49:55 -0600 Subject: [PATCH] [Java.Interop] use StringComparer.Ordinal for Dictionary Context: https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings#ordinal-string-operations When profiling startup for a Xamarin.Forms app, I noticed some heavy calls to `Dictionary`: Total(ms) Self(ms) Calls Method name 392 49 14826 System.Collections.Generic.Dictionary`2:FindEntry (TKey_REF) 377 18 12744 System.Collections.Generic.Dictionary`2:TryGetValue (TKey_REF,TValue_REF&) 129 14 3827 System.Collections.Generic.Dictionary`2:TryInsert (TKey_REF,TValue_REF,System.Collections.Generic.InsertionBehavior) Quite a few of these are coming from Java.Interop itself, and I noticed we were using the default constructor for `Dictionary`. This means we are using a culture-aware string comparison for every Java member we lookup. I could see a small difference in a Release build of the Xamarin.Forms integration project in xamarin-android: Before: 01-03 09:26:24.830 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +763ms 01-03 09:26:28.865 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +754ms 01-03 09:26:32.865 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +754ms 01-03 09:26:36.898 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +759ms 01-03 09:26:40.915 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +760ms 01-03 09:26:44.863 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +752ms 01-03 09:26:48.864 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +759ms 01-03 09:26:52.881 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +753ms 01-03 09:26:56.864 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +746ms 01-03 09:27:00.864 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +760ms Average(ms): 756 Std Err(ms): 1.60554594383897 Std Dev(ms): 5.07718207057594 After: 01-03 09:48:00.954 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +745ms 01-03 09:48:04.988 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +754ms 01-03 09:48:08.987 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +761ms 01-03 09:48:12.953 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +735ms 01-03 09:48:16.987 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +752ms 01-03 09:48:20.971 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +757ms 01-03 09:48:24.938 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +760ms 01-03 09:48:28.921 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +767ms 01-03 09:48:32.888 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +745ms 01-03 09:48:36.922 1473 1503 I ActivityTaskManager: Displayed Xamarin.Forms_Performance_Integration/xamarin.forms.performance.integration.MainActivity: +757ms Average(ms): 753.3 Std Err(ms): 2.97040962382856 Std Dev(ms): 9.39325999498222 I would guess this saves ~2ms to startup? I also toyed with changing the `capacity` value to 0, but that seemed to make things worse. I don't think there is a way for us to "guess" the default capacity for these dictionaries, so let's let the BCL do that. --- .../Java.Interop/JniPeerMembers.JniInstanceFields.cs | 2 +- .../Java.Interop/JniPeerMembers.JniInstanceMethods.cs | 2 +- src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticFields.cs | 2 +- .../Java.Interop/JniPeerMembers.JniStaticMethods.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceFields.cs b/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceFields.cs index 9d9676fd1..9c32951f5 100644 --- a/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceFields.cs +++ b/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceFields.cs @@ -13,7 +13,7 @@ internal JniInstanceFields (JniPeerMembers members) readonly JniPeerMembers Members; - Dictionary InstanceFields = new Dictionary(); + Dictionary InstanceFields = new Dictionary(StringComparer.Ordinal); internal void Dispose () { diff --git a/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs b/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs index c48204987..c13bb5b0b 100644 --- a/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs +++ b/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs @@ -35,7 +35,7 @@ internal JniType JniPeerType { readonly Type DeclaringType; - Dictionary InstanceMethods = new Dictionary(); + Dictionary InstanceMethods = new Dictionary(StringComparer.Ordinal); Dictionary SubclassConstructors = new Dictionary (); internal void Dispose () diff --git a/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticFields.cs b/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticFields.cs index 91be0b7ce..8e1a3a3be 100644 --- a/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticFields.cs +++ b/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticFields.cs @@ -13,7 +13,7 @@ internal JniStaticFields (JniPeerMembers members) readonly JniPeerMembers Members; - Dictionary StaticFields = new Dictionary(); + Dictionary StaticFields = new Dictionary(StringComparer.Ordinal); public JniFieldInfo GetFieldInfo (string encodedMember) { diff --git a/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs b/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs index 4879330e0..bfd0b7045 100644 --- a/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs +++ b/src/Java.Interop/Java.Interop/JniPeerMembers.JniStaticMethods.cs @@ -13,7 +13,7 @@ internal JniStaticMethods (JniPeerMembers members) internal readonly JniPeerMembers Members; - Dictionary StaticMethods = new Dictionary(); + Dictionary StaticMethods = new Dictionary(StringComparer.Ordinal); internal void Dispose () {