Make FrozenHashTable non-generic (#81603)
authorStephen Toub <stoub@microsoft.com>
Sat, 4 Feb 2023 03:18:53 +0000 (22:18 -0500)
committerGitHub <noreply@github.com>
Sat, 4 Feb 2023 03:18:53 +0000 (22:18 -0500)
With some tweaks to how it's defined, we can avoid making it generic at all, which helps to reduce native aot compilation size when ToFrozenDictionary/Set is used with multiple generic instantiations.

src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenHashTable.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/Int32/Int32FrozenDictionary.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/Int32/Int32FrozenSet.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/ItemsFrozenSet.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/KeysAndValuesFrozenDictionary.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenDictionary.cs
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/String/OrdinalStringFrozenSet.cs

index 5ad2bde..0e12af3 100644 (file)
@@ -36,29 +36,28 @@ namespace System.Collections.Frozen
         }
 
         /// <summary>Initializes a frozen hash table.</summary>
-        /// <param name="entries">The set of entries to track from the hash table.</param>
-        /// <param name="hasher">A delegate that produces a hash code for a given entry.</param>
-        /// <param name="setter">A delegate that assigns the index to a specific entry.</param>
+        /// <param name="entriesLength">The number of entries to track from the hash table.</param>
+        /// <param name="hashAtIndex">A delegate that produces a hash code for a given entry. It's passed the index of the entry and returns that entry's hash code.</param>
+        /// <param name="storeDestIndexFromSrcIndex">A delegate that assigns the index to a specific entry. It's passed the destination and source indices.</param>
         /// <param name="optimizeForReading">true to spend additional effort tuning for subsequent read speed on the table; false to prioritize construction time.</param>
-        /// <typeparam name="T">The type of elements in the hash table.</typeparam>
         /// <remarks>
         /// This method will iterate through the incoming entries and will invoke the hasher on each once.
         /// It will then determine the optimal number of hash buckets to allocate and will populate the
-        /// bucket table. In the process of doing so, it calls out to the <paramref name="setter"/> to indicate
+        /// bucket table. In the process of doing so, it calls out to the <paramref name="storeDestIndexFromSrcIndex"/> to indicate
         /// the resulting index for that entry. <see cref="FindMatchingEntries(int, out int, out int)"/>
         /// then uses this index to reference individual entries by indexing into <see cref="HashCodes"/>.
         /// </remarks>
         /// <returns>A frozen hash table.</returns>
-        public static FrozenHashTable Create<T>(T[] entries, Func<T, int> hasher, Action<int, T> setter, bool optimizeForReading = true)
+        public static FrozenHashTable Create(int entriesLength, Func<int, int> hashAtIndex, Action<int, int> storeDestIndexFromSrcIndex, bool optimizeForReading = true)
         {
-            Debug.Assert(entries.Length != 0);
+            Debug.Assert(entriesLength != 0);
 
             // Calculate the hashcodes for every entry.
-            int[] arrayPoolHashCodes = ArrayPool<int>.Shared.Rent(entries.Length);
-            Span<int> hashCodes = arrayPoolHashCodes.AsSpan(0, entries.Length);
-            for (int i = 0; i < entries.Length; i++)
+            int[] arrayPoolHashCodes = ArrayPool<int>.Shared.Rent(entriesLength);
+            Span<int> hashCodes = arrayPoolHashCodes.AsSpan(0, entriesLength);
+            for (int i = 0; i < entriesLength; i++)
             {
-                hashCodes[i] = hasher(entries[i]);
+                hashCodes[i] = hashAtIndex(i);
             }
 
             // Determine how many buckets to use.  This might be fewer than the number of entries
@@ -113,7 +112,7 @@ namespace System.Collections.Frozen
                 while (index >= 0)
                 {
                     hashtableHashcodes[count] = hashCodes[index];
-                    setter(count, entries[index]);
+                    storeDestIndexFromSrcIndex(count, index);
                     count++;
                     bucketCount++;
 
index 43214af..bedcbb6 100644 (file)
@@ -28,9 +28,9 @@ namespace System.Collections.Frozen
             _values = new TValue[entries.Length];
 
             _hashTable = FrozenHashTable.Create(
-                entries,
-                pair => pair.Key,
-                (index, pair) => _values[index] = pair.Value);
+                entries.Length,
+                index => entries[index].Key,
+                (destIndex, srcIndex) => _values[destIndex] = entries[srcIndex].Value);
         }
 
         /// <inheritdoc />
index 7e47486..7f1da9d 100644 (file)
@@ -1,9 +1,9 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Buffers;
 using System.Collections.Generic;
 using System.Diagnostics;
-using System.Linq;
 
 namespace System.Collections.Frozen
 {
@@ -21,10 +21,16 @@ namespace System.Collections.Frozen
             Debug.Assert(source.Count != 0);
             Debug.Assert(ReferenceEquals(source.Comparer, EqualityComparer<int>.Default));
 
+            int count = source.Count;
+            int[] entries = ArrayPool<int>.Shared.Rent(count);
+            source.CopyTo(entries);
+
             _hashTable = FrozenHashTable.Create(
-                source.ToArray(),
-                item => item,
-                (_, _) => { });
+                count,
+                index => entries[index],
+                delegate { });
+
+            ArrayPool<int>.Shared.Return(entries);
         }
 
         /// <inheritdoc />
index 61fdf6a..abf9188 100644 (file)
@@ -23,9 +23,9 @@ namespace System.Collections.Frozen
             _items = new T[entries.Length];
 
             _hashTable = FrozenHashTable.Create(
-                entries,
-                o => o is null ? 0 : Comparer.GetHashCode(o),
-                (index, item) => _items[index] = item,
+                entries.Length,
+                index => entries[index] is T t ? Comparer.GetHashCode(t) : 0,
+                (destIndex, srcIndex) => _items[destIndex] = entries[srcIndex],
                 optimizeForReading);
         }
 
index f3bd495..fb168cf 100644 (file)
@@ -25,12 +25,12 @@ namespace System.Collections.Frozen
             _values = new TValue[entries.Length];
 
             _hashTable = FrozenHashTable.Create(
-                entries,
-                pair => Comparer.GetHashCode(pair.Key),
-                (index, pair) =>
+                entries.Length,
+                index => Comparer.GetHashCode(entries[index].Key),
+                (destIndex, srcIndex) =>
                 {
-                    _keys[index] = pair.Key;
-                    _values[index] = pair.Value;
+                    _keys[destIndex] = entries[srcIndex].Key;
+                    _values[destIndex] = entries[srcIndex].Value;
                 },
                 optimizeForReading);
         }
index 4ff5c90..b9f7dfd 100644 (file)
@@ -41,12 +41,12 @@ namespace System.Collections.Frozen
             HashCount = hashCount;
 
             _hashTable = FrozenHashTable.Create(
-                entries,
-                pair => GetHashCode(pair.Key),
-                (index, pair) =>
+                entries.Length,
+                index => GetHashCode(entries[index].Key),
+                (destIndex, srcIndex) =>
                 {
-                    _keys[index] = pair.Key;
-                    _values[index] = pair.Value;
+                    _keys[destIndex] = entries[srcIndex].Key;
+                    _values[destIndex] = entries[srcIndex].Value;
                 });
         }
 
index bd1ee3c..f4c7700 100644 (file)
@@ -31,9 +31,9 @@ namespace System.Collections.Frozen
             HashCount = hashCount;
 
             _hashTable = FrozenHashTable.Create(
-                entries,
-                GetHashCode,
-                (index, item) => _items[index] = item);
+                entries.Length,
+                index => GetHashCode(entries[index]),
+                (destIndex, srcIndex) => _items[destIndex] = entries[srcIndex]);
         }
 
         private protected int HashIndex { get; }