Skip to content

Make HashHelpers.Primes private; use GetPrime in FrozenHashTable#126049

Closed
danmoseley wants to merge 1 commit intodotnet:mainfrom
danmoseley:hashhelpers-primes-private
Closed

Make HashHelpers.Primes private; use GetPrime in FrozenHashTable#126049
danmoseley wants to merge 1 commit intodotnet:mainfrom
danmoseley:hashhelpers-primes-private

Conversation

@danmoseley
Copy link
Member

HashHelpers.Primes is an implementation detail — the precomputed prime table backing GetPrime. It was exposed as internal (in System.Private.CoreLib) and public (in MetadataLoadContext), but its only external consumer was FrozenHashTable, which iterated the table directly for collision-tuning bucket selection.

This PR makes Primes private and replaces the direct table access in FrozenHashTable with a simple GetPrime loop. GetPrime already encapsulates the same table scan, and trial division past the table is negligible on this cold construction path (~1300 divisions at 7M).

Changes

  • Make Primes private in System.Private.CoreLib HashHelpers
  • Make Primes private in MetadataLoadContext HashHelpers
  • Replace index-based primes iteration in FrozenHashTable.CalcNumBuckets with GetPrime loop (−29 lines)
  • Update stale comment in ObjectIDGenerator

Note

This PR was generated with the assistance of GitHub Copilot.

@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-collections
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens encapsulation around HashHelpers by making the precomputed prime table (Primes) private and updating FrozenHashTable to rely on HashHelpers.GetPrime rather than iterating the table directly.

Changes:

  • Make HashHelpers.Primes private in System.Private.CoreLib and System.Reflection.MetadataLoadContext.
  • Update FrozenHashTable.CalcNumBuckets to iterate candidate bucket sizes via repeated GetPrime calls instead of scanning Primes by index.
  • Update a stale comment in ObjectIDGenerator to no longer reference HashHelpers.Primes.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/libraries/System.Runtime.Serialization.Formatters/src/System/Runtime/Serialization/ObjectIDGenerator.cs Updates initialization comment to remove reliance on HashHelpers.Primes.
src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/HashHelpers.cs Makes the prime table span private within HashHelpers.
src/libraries/System.Private.CoreLib/src/System/Collections/HashHelpers.cs Makes the prime table span private within CoreLib HashHelpers.
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenHashTable.cs Reworks bucket-count candidate iteration to use GetPrime instead of direct prime-table access.

Copilot AI review requested due to automatic review settings March 24, 2026 18:45
@danmoseley danmoseley force-pushed the hashhelpers-primes-private branch from 83f7496 to e2b1e1d Compare March 24, 2026 18:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment on lines 208 to 215
// If the smallest prime >= minNumBuckets already exceeds our budget,
// there's no range of primes to search — just use it directly.
int startPrime = HashHelpers.GetPrime((int)minNumBuckets);
if (startPrime > maxNumBuckets)
{
Debug.Assert(maxPrimeIndexExclusive != 0);
maxNumBuckets = primes[maxPrimeIndexExclusive - 1];
return startPrime;
}

Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “very large inputs” comment doesn’t match what the code actually checks. startPrime > maxNumBuckets is effectively unreachable with the current min/max policies (min is ~2x unique, max is >=3x or 16x unique), so this won’t skip the collision-tuning loop when minNumBuckets exceeds the precomputed prime table. Consider adding an explicit guard for the “beyond precomputed primes” case (e.g., via a HashHelpers helper that can detect/limit to the table) and update the comment accordingly.

Copilot uses AI. Check for mistakes.
Comment on lines 224 to 227
for (numBuckets = startPrime;
numBuckets <= maxNumBuckets;
numBuckets = HashHelpers.GetPrime(numBuckets + 1))
{
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The for loop advances via HashHelpers.GetPrime(numBuckets + 1). Once numBuckets reaches the end of HashHelpers’ precomputed prime table, GetPrime switches to trial division and this loop can end up iterating over a large number of consecutive primes up to maxNumBuckets (and running the full collision-counting pass each time), which can be prohibitively expensive for large frozen collections. To avoid a potential perf cliff, cap the search to the precomputed table (or a fixed max iteration count) and fall back to a single GetPrime(...) choice when the range goes beyond the table.

Copilot uses AI. Check for mistakes.
@danmoseley danmoseley marked this pull request as draft March 24, 2026 18:52
HashHelpers.Primes is an implementation detail that was exposed as
internal/public. The only external consumer was FrozenHashTable, which
iterated the table directly for collision-tuning bucket selection.

Replace the direct table access with GetPrime calls in a simple loop.
Expose only MaxPrecomputedPrime so callers can bail before hitting
trial-division territory.

- Make Primes private in System.Private.CoreLib and MetadataLoadContext
- Add MaxPrecomputedPrime property derived from the table
- Replace index-based primes iteration in FrozenHashTable with GetPrime loop
- Add XML doc comments to HashHelpers public methods
- Update stale comment in ObjectIDGenerator

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danmoseley danmoseley force-pushed the hashhelpers-primes-private branch from e2b1e1d to 086472e Compare March 24, 2026 19:10
@danmoseley
Copy link
Member Author

abandoning for now. If it matters, I'll pick up sometime after the other PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants