From 2270a0a8b9a8dc9037a608f9e17ff4e8b6a2cb77 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Tue, 8 Jul 2025 18:31:16 +0200 Subject: [PATCH 1/4] Optimize Dictionary.FindValue for constant keys --- .../src/System/Collections/Generic/Dictionary.cs | 2 +- .../Collections/Generic/NonRandomizedStringEqualityComparer.cs | 2 +- .../Collections/Generic/RandomizedStringEqualityComparer.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs index c890bbed83c7ef..25a453341449f7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs @@ -409,7 +409,7 @@ internal ref TValue FindValue(TKey key) if (typeof(TKey).IsValueType && // comparer can only be null for value types; enable JIT to eliminate entire if block for ref types comparer == null) { - uint hashCode = (uint)key.GetHashCode(); + uint hashCode = (uint)EqualityComparer.Default.GetHashCode(key); int i = GetBucket(hashCode); Entry[]? entries = _entries; uint collisionCount = 0; diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs index 9bc99f7ef4c826..ff903b17f07d20 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/NonRandomizedStringEqualityComparer.cs @@ -112,7 +112,7 @@ internal OrdinalIgnoreCaseComparer(IEqualityComparer wrappedComparer) : { } - public override bool Equals(string? x, string? y) => string.EqualsOrdinalIgnoreCase(x, y); + public override bool Equals(string? x, string? y) => string.Equals(x, y, StringComparison.OrdinalIgnoreCase); public override int GetHashCode(string? obj) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/RandomizedStringEqualityComparer.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/RandomizedStringEqualityComparer.cs index d6d79bb96b9e7b..e613eb8614dcac 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/RandomizedStringEqualityComparer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/RandomizedStringEqualityComparer.cs @@ -102,7 +102,7 @@ internal OrdinalIgnoreCaseComparer(IEqualityComparer wrappedComparer) string IAlternateEqualityComparer, string?>.Create(ReadOnlySpan span) => span.ToString(); - public override bool Equals(string? x, string? y) => string.EqualsOrdinalIgnoreCase(x, y); + public override bool Equals(string? x, string? y) => string.Equals(x, y, StringComparison.OrdinalIgnoreCase); bool IAlternateEqualityComparer, string?>.Equals(ReadOnlySpan alternate, string? other) { From 7621f6a3aa1bcba34050fd26c272f464d2a78925 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Tue, 8 Jul 2025 18:59:04 +0200 Subject: [PATCH 2/4] remove dead code --- .../src/System/String.Comparison.cs | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs b/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs index f9261106b34c73..458aa815cd6560 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs @@ -46,26 +46,6 @@ ref Unsafe.Add(ref strA.GetRawStringData(), (nint)(uint)indexA /* force zero-ext ref Unsafe.Add(ref strB.GetRawStringData(), (nint)(uint)indexB /* force zero-extension */), countB); } - internal static bool EqualsOrdinalIgnoreCase(string? strA, string? strB) - { - if (ReferenceEquals(strA, strB)) - { - return true; - } - - if (strA is null || strB is null) - { - return false; - } - - if (strA.Length != strB.Length) - { - return false; - } - - return EqualsOrdinalIgnoreCaseNoLengthCheck(strA, strB); - } - private static bool EqualsOrdinalIgnoreCaseNoLengthCheck(string strA, string strB) { Debug.Assert(strA.Length == strB.Length); From b8568bbc27adbbf09aeb4ae66326e9c58755beaa Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 10 Jul 2025 23:27:16 +0200 Subject: [PATCH 3/4] Update Dictionary.cs --- .../src/System/Collections/Generic/Dictionary.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs index 25a453341449f7..d49e937a0f3359 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs @@ -409,6 +409,7 @@ internal ref TValue FindValue(TKey key) if (typeof(TKey).IsValueType && // comparer can only be null for value types; enable JIT to eliminate entire if block for ref types comparer == null) { + // TODO: Replace with just key.GetHashCode once https://github.com/dotnet/runtime/pull/117427 is resolved. uint hashCode = (uint)EqualityComparer.Default.GetHashCode(key); int i = GetBucket(hashCode); Entry[]? entries = _entries; From a58c45953fa057058b53f99d1a9c537f7da2df93 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Thu, 10 Jul 2025 23:28:17 +0200 Subject: [PATCH 4/4] oops, wrong link --- .../src/System/Collections/Generic/Dictionary.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs index d49e937a0f3359..c103bd8fe4304c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/Dictionary.cs @@ -409,7 +409,7 @@ internal ref TValue FindValue(TKey key) if (typeof(TKey).IsValueType && // comparer can only be null for value types; enable JIT to eliminate entire if block for ref types comparer == null) { - // TODO: Replace with just key.GetHashCode once https://github.com/dotnet/runtime/pull/117427 is resolved. + // TODO: Replace with just key.GetHashCode once https://github.com/dotnet/runtime/issues/117521 is resolved. uint hashCode = (uint)EqualityComparer.Default.GetHashCode(key); int i = GetBucket(hashCode); Entry[]? entries = _entries;