From 1b6d9f992f5754c706686fff965f5502f077f0c4 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Sat, 21 Mar 2026 14:41:39 -0600 Subject: [PATCH] Improve HashSet lookup performance via bounds check elimination Change while (i >= 0) to while ((uint)i < (uint)entries.Length) in all 7 hash-chain traversal loops, matching the pattern already used in Dictionary. This lets the JIT eliminate the separate bounds check on entries[i], as the unsigned loop condition serves as both loop exit and implicit range check. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../src/System/Collections/Generic/HashSet.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs index 14cc4bb26b3c67..facfd3c45cc9fc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs @@ -233,7 +233,7 @@ private int FindItemIndex(T item) // ValueType: Devirtualize with EqualityComparer.Default intrinsic int hashCode = item!.GetHashCode(); int i = GetBucketRef(hashCode) - 1; // Value in _buckets is 1-based - while (i >= 0) + while ((uint)i < (uint)entries.Length) { ref Entry entry = ref entries[i]; if (entry.HashCode == hashCode && EqualityComparer.Default.Equals(entry.Value, item)) @@ -255,7 +255,7 @@ private int FindItemIndex(T item) Debug.Assert(comparer is not null); int hashCode = item != null ? comparer.GetHashCode(item) : 0; int i = GetBucketRef(hashCode) - 1; // Value in _buckets is 1-based - while (i >= 0) + while ((uint)i < (uint)entries.Length) { ref Entry entry = ref entries[i]; if (entry.HashCode == hashCode && comparer.Equals(entry.Value, item)) @@ -309,7 +309,7 @@ public bool Remove(T item) ref int bucket = ref GetBucketRef(hashCode); int i = bucket - 1; // Value in buckets is 1-based - while (i >= 0) + while ((uint)i < (uint)entries.Length) { ref Entry entry = ref entries[i]; @@ -473,7 +473,7 @@ public bool Add(TAlternate item) hashCode = comparer.GetHashCode(item); bucket = ref set.GetBucketRef(hashCode); int i = bucket - 1; // Value in _buckets is 1-based - while (i >= 0) + while ((uint)i < (uint)entries.Length) { ref Entry entry = ref entries[i]; if (entry.HashCode == hashCode && comparer.Equals(item, entry.Value)) @@ -556,7 +556,7 @@ public bool Remove(TAlternate item) ref int bucket = ref set.GetBucketRef(hashCode); int i = bucket - 1; // Value in buckets is 1-based - while (i >= 0) + while ((uint)i < (uint)entries.Length) { ref Entry entry = ref entries[i]; @@ -1441,7 +1441,7 @@ private bool AddIfNotPresent(T value, out int location) int i = bucket - 1; // Value in _buckets is 1-based // ValueType: Devirtualize with EqualityComparer.Default intrinsic - while (i >= 0) + while ((uint)i < (uint)entries.Length) { ref Entry entry = ref entries[i]; if (entry.HashCode == hashCode && EqualityComparer.Default.Equals(entry.Value, value)) @@ -1465,7 +1465,7 @@ private bool AddIfNotPresent(T value, out int location) hashCode = value != null ? comparer.GetHashCode(value) : 0; bucket = ref GetBucketRef(hashCode); int i = bucket - 1; // Value in _buckets is 1-based - while (i >= 0) + while ((uint)i < (uint)entries.Length) { ref Entry entry = ref entries[i]; if (entry.HashCode == hashCode && comparer.Equals(entry.Value, value))