Skip to content

feat(collections): add FrozenCeleritySet build-once string set (#22) - #170

Merged
marius-bughiu merged 1 commit into
mainfrom
feat/issue-22-frozen-celerity-set
Jun 6, 2026
Merged

feat(collections): add FrozenCeleritySet build-once string set (#22)#170
marius-bughiu merged 1 commit into
mainfrom
feat/issue-22-frozen-celerity-set

Conversation

@marius-bughiu

Copy link
Copy Markdown
Owner

What & why

Implements FrozenCeleritySet — the build-once, read-many string set, completing the 1.2.0 frozen-collections family (FrozenCelerityDictionaryFrozenCeleritySet). Closes the frozen-collections umbrella issue #22 (the dictionary half shipped earlier via #62; this is the set counterpart that made the family whole, mirroring the IntDictionary → IntSet / CelerityDictionary → CeleritySet parity pattern).

It is the only open issue on the current milestone (1.2.0); there were no open bugs or priority:high items, so this is the normal tier-(b) pick: the next planned feature on the current milestone.

Design

  • Immutable, build-once. Constructed from an IEnumerable<string>; the constructor searches a small parameter space (power-of-two table size × a per-build mixing seed) for a perfect (collision-free) placement. On the fast path (IsPerfectlyHashed == true) Contains is a single hash + index + equality check.
  • Always correct. When two distinct elements share the chosen hasher's raw 32-bit code (e.g. "A" / "Ł" under low-byte StringFnV1AHasher), a perfect layout is impossible, so the build falls back to open-addressed linear probing — still correct, just not single-probe.
  • Set semantics. Duplicate elements are silently deduplicated (matching BCL FrozenSet<T> and the mutable CeleritySet) — the one intentional contract difference from the frozen dictionary, which rejects duplicate keys.
  • Implements IReadOnlySet<string> (SetEquals / IsSubsetOf / IsProperSubsetOf / IsSupersetOf / IsProperSupersetOf / Overlaps). The null element is stored out-of-band; the empty string is an ordinary element. AOT-safe; hot-path membership is allocation-free.

Parity rollout (all in this PR)

  • Collectionsrc/Celerity/Collections/FrozenCeleritySet.cs (convenience type + <THasher> overload).
  • Dedicated testsFrozenCeleritySetTests mirroring FrozenCelerityDictionaryTests + the full set-algebra matrix.
  • Differential layer — CsCheck model property test (CollectionModelPropertyTests.FrozenCeleritySet_ShouldMatch_BclHashSet) and a Celerity.Fuzz target (FrozenCeleritySet), both vs a HashSet<string> oracle.
  • Cross-collection shared testsSetIEnumerableConstructorTests extended (the mirror of the dict IEnumerableConstructorTests). Inapplicable shared files (SetConstructorValidationTests — no loadFactor/capacity; TryAdd* — immutable; dictionary-only files) noted in the changelog.
  • BenchmarkFrozenCeleritySetBenchmark (Build + Contains vs BCL FrozenSet<string>), registered in Program.cs CoreBenchmarks.
  • Dashboard — ship card in web/index.html; COLLECTIONS entry in web/dev/bench/index.html and detail.html (FrozenSet added to BCL_TYPES so the baseline series resolves).
  • Docsdocs/api/collections.md section + README (list, decision table, quick start); AOT smoke test exercises the new instantiations.
  • CHANGELOG + ROADMAP updated.

Test plan

  • dotnet build (Debug + Release) — clean (the 3 warnings are pre-existing, unrelated files).
  • dotnet test2166 passed, 0 failed (Debug and Release), including the 2000-iteration CsCheck property test.
  • Celerity.Fuzz --target FrozenCeleritySet --iterations 3000 — all pass.
  • Celerity.AotSmokeTest — all checks pass (validates the new generic instantiations).
  • CI matrix (Windows / Linux / macOS), Native AOT publish job, coverage gate, and the nightly fuzz soak run on CI.
  • On merge to main, the benchmark workflow republishes data.js to gh-pages and the dashboard surfaces FrozenCeleritySet (Build / Contains vs FrozenSet<string>).

🤖 Generated with Claude Code

Completes the 1.2.0 frozen-collections family: FrozenCeleritySet is the
set counterpart of FrozenCelerityDictionary. Build-once, immutable, with a
perfect-hash (collision-free) layout searched at construction and a
linear-probing fallback when the chosen hasher collides two elements' raw
codes, so Contains is single-probe on the fast path and always correct.

Unlike the frozen dictionary (which rejects duplicate keys), duplicate
elements are silently deduplicated — set semantics, matching BCL FrozenSet
and the mutable CeleritySet. Implements IReadOnlySet<string> (SetEquals,
IsSubsetOf, IsSupersetOf, Overlaps, ...). The null element is stored
out-of-band; the empty string is an ordinary element.

Full parity rollout in one PR:
- Collection: src/Celerity/Collections/FrozenCeleritySet.cs (convenience
  type + <THasher> overload).
- Dedicated tests: FrozenCeleritySetTests (construction, null element,
  dedupe, perfect vs fallback, set-algebra matrix, enumeration, non-generic).
- Differential layer: CsCheck model property test + Celerity.Fuzz target,
  both vs a HashSet<string> oracle.
- Cross-collection shared tests: SetIEnumerableConstructorTests extended
  (mirror of the dict IEnumerableConstructorTests); inapplicable mutation /
  loadFactor shared files noted.
- Benchmark: FrozenCeleritySetBenchmark (Build + Contains vs BCL
  FrozenSet<string>), registered in Program.cs CoreBenchmarks.
- Dashboard: ship card + COLLECTIONS entry in web/index.html,
  web/dev/bench/index.html, web/dev/bench/detail.html (FrozenSet added to
  BCL_TYPES).
- Docs: docs/api/collections.md section, README list / decision table /
  quick start; AOT smoke test exercises the new instantiations.
- CHANGELOG + ROADMAP updated.

All 2166 tests pass; AOT smoke test passes; 3000 fuzz cases pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown

Coverage

Metric Value
Line 100% (3255/3255)
Branch 100% (1132/1132)

@marius-bughiu
marius-bughiu merged commit 360c1d2 into main Jun 6, 2026
6 checks passed
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Benchmarks

2 regressions ⚠️ vs main, 2 improvements ✅ (rows past ±10% beyond noise).

Highlights

Benchmark This PR StdDev main Δ
SmallDictionaryBenchmark.Dictionary_Insert(ItemCount: 64) 788.1 ns 9.9 ns 891.3 ns -11.6% ✅
CelerityDictionaryBenchmark.Dictionary_Insert(ItemCount: 1000) 16.14 μs 548.6 ns 13.41 μs +20.4% ⚠️
SmallDictionaryBenchmark.Dictionary_Remove(ItemCount: 8) 1.73 μs 279.7 ns 1.31 μs +32.6% ⚠️
CelerityMultiMapBenchmark.CelerityMultiMap_Remove(ItemCount: 100000) 1.87 ms 155.68 μs 2.50 ms -25.1% ✅
Collections (124)
Benchmark This PR StdDev main Δ
CeleritySetBenchmark.HashSet_Add(ItemCount: 1000) 12.53 μs 291.5 ns 12.80 μs -2.1%
IntSetBenchmark.HashSet_Add(ItemCount: 1000) 12.68 μs 191.8 ns 12.86 μs -1.4%
LongSetBenchmark.HashSet_Add(ItemCount: 1000) 13.18 μs 134.6 ns 13.58 μs -3.0%
CeleritySetBenchmark.CeleritySet_Add(ItemCount: 1000) 8.89 μs 74.3 ns 9.04 μs -1.6%
IntSetBenchmark.IntSet_Add(ItemCount: 1000) 8.78 μs 110.3 ns 8.93 μs -1.7%
LongSetBenchmark.LongSet_Add(ItemCount: 1000) 9.65 μs 300.7 ns 9.24 μs +4.4%
CeleritySetBenchmark.HashSet_Add(ItemCount: 100000) 5.05 ms 99.50 μs 5.06 ms -0.1%
IntSetBenchmark.HashSet_Add(ItemCount: 100000) 5.10 ms 134.48 μs 5.04 ms +1.2%
LongSetBenchmark.HashSet_Add(ItemCount: 100000) 4.51 ms 255.22 μs 4.58 ms -1.4%
CeleritySetBenchmark.CeleritySet_Add(ItemCount: 100000) 3.16 ms 24.63 μs 3.16 ms -0.0%
IntSetBenchmark.IntSet_Add(ItemCount: 100000) 3.16 ms 32.19 μs 3.23 ms -2.1%
LongSetBenchmark.LongSet_Add(ItemCount: 100000) 6.09 ms 53.30 μs 6.09 ms +0.0%
FrozenCelerityDictionaryBenchmark.FrozenDictionary_Build(ItemCount: 1000) 163.62 μs 1.70 μs 163.01 μs +0.4%
FrozenCeleritySetBenchmark.FrozenSet_Build(ItemCount: 1000) 165.87 μs 865.2 ns 166.77 μs -0.5%
FrozenCelerityDictionaryBenchmark.FrozenCelerityDictionary_Build(ItemCount: 1000) 3.64 ms 104.41 μs 3.75 ms -2.9%
FrozenCeleritySetBenchmark.FrozenCeleritySet_Build(ItemCount: 1000) 2.58 ms 120.08 μs 2.61 ms -1.2%
FrozenCelerityDictionaryBenchmark.FrozenDictionary_Build(ItemCount: 100000) 29.66 ms 333.69 μs 29.66 ms +0.0%
FrozenCeleritySetBenchmark.FrozenSet_Build(ItemCount: 100000) 29.46 ms 615.87 μs 29.48 ms -0.0%
FrozenCelerityDictionaryBenchmark.FrozenCelerityDictionary_Build(ItemCount: 100000) 1.83 s 64.02 ms 2.00 s -8.8%
FrozenCeleritySetBenchmark.FrozenCeleritySet_Build(ItemCount: 100000) 1.52 s 68.80 ms 1.52 s +0.0%
CeleritySetBenchmark.HashSet_Contains(ItemCount: 1000) 4.75 μs 15.6 ns 4.76 μs -0.2%
FrozenCeleritySetBenchmark.FrozenSet_Contains(ItemCount: 1000) 6.84 μs 16.5 ns 6.86 μs -0.3%
IntSetBenchmark.HashSet_Contains(ItemCount: 1000) 4.74 μs 4.7 ns 4.77 μs -0.7%
LongSetBenchmark.HashSet_Contains(ItemCount: 1000) 4.80 μs 44.1 ns 4.80 μs +0.1%
CeleritySetBenchmark.CeleritySet_Contains(ItemCount: 1000) 1.83 μs 26.7 ns 1.80 μs +1.9%
FrozenCeleritySetBenchmark.FrozenCeleritySet_Contains(ItemCount: 1000) 96.52 μs 1.93 μs 94.92 μs +1.7%
IntSetBenchmark.IntSet_Contains(ItemCount: 1000) 1.80 μs 7.7 ns 1.80 μs -0.2%
LongSetBenchmark.LongSet_Contains(ItemCount: 1000) 2.01 μs 7.8 ns 2.01 μs -0.0%
CeleritySetBenchmark.HashSet_Contains(ItemCount: 100000) 1.58 ms 6.09 μs 1.57 ms +0.8%
FrozenCeleritySetBenchmark.FrozenSet_Contains(ItemCount: 100000) 1.94 ms 4.74 μs 1.97 ms -1.3%
IntSetBenchmark.HashSet_Contains(ItemCount: 100000) 1.57 ms 11.16 μs 1.57 ms -0.3%
LongSetBenchmark.HashSet_Contains(ItemCount: 100000) 1.58 ms 54.62 μs 1.60 ms -1.1%
CeleritySetBenchmark.CeleritySet_Contains(ItemCount: 100000) 586.54 μs 2.31 μs 584.44 μs +0.4%
FrozenCeleritySetBenchmark.FrozenCeleritySet_Contains(ItemCount: 100000) 7.32 ms 43.16 μs 7.27 ms +0.7%
IntSetBenchmark.IntSet_Contains(ItemCount: 100000) 571.27 μs 1.96 μs 573.63 μs -0.4%
LongSetBenchmark.LongSet_Contains(ItemCount: 100000) 639.37 μs 1.32 μs 642.08 μs -0.4%
SmallDictionaryBenchmark.Dictionary_Insert(ItemCount: 8) 166.6 ns 2.4 ns 169.4 ns -1.7%
SmallDictionaryBenchmark.SmallDictionary_Insert(ItemCount: 8) 86.5 ns 1.1 ns 88.6 ns -2.4%
SmallDictionaryBenchmark.Dictionary_Insert(ItemCount: 64) 788.1 ns 9.9 ns 891.3 ns -11.6% ✅
SmallDictionaryBenchmark.SmallDictionary_Insert(ItemCount: 64) 1.07 μs 18.0 ns 1.08 μs -1.1%
CelerityDictionaryBenchmark.Dictionary_Insert(ItemCount: 1000) 16.14 μs 548.6 ns 13.41 μs +20.4% ⚠️
CelerityMultiMapBenchmark.Dictionary_Insert(ItemCount: 1000) 29.15 μs 120.6 ns 30.59 μs -4.7%
IntDictionaryBenchmark.Dictionary_Insert(ItemCount: 1000) 13.46 μs 885.3 ns 13.60 μs -1.1%
LongDictionaryBenchmark.Dictionary_Insert(ItemCount: 1000) 14.94 μs 115.9 ns 15.49 μs -3.6%
RobinHoodDictionaryBenchmark.Dictionary_Insert(ItemCount: 1000) 13.73 μs 373.8 ns 13.13 μs +4.6%
CelerityDictionaryBenchmark.CelerityDictionary_Insert(ItemCount: 1000) 12.15 μs 241.9 ns 11.93 μs +1.8%
CelerityMultiMapBenchmark.CelerityMultiMap_Insert(ItemCount: 1000) 37.68 μs 327.4 ns 39.52 μs -4.7%
IntDictionaryBenchmark.IntDictionary_Insert(ItemCount: 1000) 10.74 μs 827.7 ns 11.53 μs -6.9%
LongDictionaryBenchmark.LongDictionary_Insert(ItemCount: 1000) 11.68 μs 215.9 ns 12.27 μs -4.8%
RobinHoodDictionaryBenchmark.RobinHoodDictionary_Insert(ItemCount: 1000) 16.62 μs 387.9 ns 16.38 μs +1.4%
CelerityDictionaryBenchmark.Dictionary_Insert(ItemCount: 100000) 4.16 ms 55.55 μs 4.11 ms +1.3%
CelerityMultiMapBenchmark.Dictionary_Insert(ItemCount: 100000) 12.61 ms 549.75 μs 12.94 ms -2.6%
IntDictionaryBenchmark.Dictionary_Insert(ItemCount: 100000) 4.11 ms 62.16 μs 4.17 ms -1.4%
LongDictionaryBenchmark.Dictionary_Insert(ItemCount: 100000) 4.97 ms 55.45 μs 5.03 ms -1.2%
RobinHoodDictionaryBenchmark.Dictionary_Insert(ItemCount: 100000) 4.69 ms 71.10 μs 4.70 ms -0.4%
CelerityDictionaryBenchmark.CelerityDictionary_Insert(ItemCount: 100000) 4.85 ms 109.94 μs 4.74 ms +2.3%
CelerityMultiMapBenchmark.CelerityMultiMap_Insert(ItemCount: 100000) 16.88 ms 120.60 μs 17.56 ms -3.8%
IntDictionaryBenchmark.IntDictionary_Insert(ItemCount: 100000) 4.84 ms 65.94 μs 4.85 ms -0.1%
LongDictionaryBenchmark.LongDictionary_Insert(ItemCount: 100000) 6.78 ms 75.80 μs 6.83 ms -0.7%
RobinHoodDictionaryBenchmark.RobinHoodDictionary_Insert(ItemCount: 100000) 7.86 ms 476.64 μs 7.77 ms +1.1%
SmallDictionaryBenchmark.Dictionary_Lookup(ItemCount: 8) 38.0 ns 1.4 ns 36.9 ns +3.1%
SmallDictionaryBenchmark.SmallDictionary_Lookup(ItemCount: 8) 28.7 ns 3.6 ns 28.5 ns +0.7%
SmallDictionaryBenchmark.Dictionary_Lookup(ItemCount: 64) 296.8 ns 0.3 ns 297.2 ns -0.1%
SmallDictionaryBenchmark.SmallDictionary_Lookup(ItemCount: 64) 1.10 μs 10.1 ns 1.09 μs +1.0%
CelerityDictionaryBenchmark.Dictionary_Lookup(ItemCount: 1000) 4.73 μs 22.6 ns 4.77 μs -0.9%
CelerityMultiMapBenchmark.Dictionary_Lookup(ItemCount: 1000) 5.05 μs 14.5 ns 5.05 μs +0.0%
FrozenCelerityDictionaryBenchmark.FrozenDictionary_Lookup(ItemCount: 1000) 7.35 μs 32.4 ns 7.33 μs +0.3%
IntDictionaryBenchmark.Dictionary_Lookup(ItemCount: 1000) 4.73 μs 20.5 ns 4.73 μs -0.0%
LongDictionaryBenchmark.Dictionary_Lookup(ItemCount: 1000) 4.90 μs 23.7 ns 4.89 μs +0.2%
RobinHoodDictionaryBenchmark.Dictionary_Lookup(ItemCount: 1000) 4.72 μs 13.0 ns 4.71 μs +0.2%
CelerityDictionaryBenchmark.CelerityDictionary_Lookup(ItemCount: 1000) 2.23 μs 5.9 ns 2.25 μs -0.8%
CelerityMultiMapBenchmark.CelerityMultiMap_Lookup(ItemCount: 1000) 2.26 μs 1.9 ns 2.26 μs -0.1%
FrozenCelerityDictionaryBenchmark.FrozenCelerityDictionary_Lookup(ItemCount: 1000) 89.99 μs 5.05 μs 94.50 μs -4.8%
IntDictionaryBenchmark.IntDictionary_Lookup(ItemCount: 1000) 2.14 μs 4.0 ns 2.16 μs -1.1%
LongDictionaryBenchmark.LongDictionary_Lookup(ItemCount: 1000) 2.28 μs 8.7 ns 2.30 μs -0.9%
RobinHoodDictionaryBenchmark.RobinHoodDictionary_Lookup(ItemCount: 1000) 2.64 μs 24.4 ns 2.61 μs +0.9%
CelerityDictionaryBenchmark.Dictionary_Lookup(ItemCount: 100000) 1.60 ms 7.36 μs 1.59 ms +0.3%
CelerityMultiMapBenchmark.Dictionary_Lookup(ItemCount: 100000) 1.74 ms 10.11 μs 1.71 ms +2.2%
FrozenCelerityDictionaryBenchmark.FrozenDictionary_Lookup(ItemCount: 100000) 1.97 ms 29.98 μs 2.02 ms -2.7%
IntDictionaryBenchmark.Dictionary_Lookup(ItemCount: 100000) 1.55 ms 45.30 μs 1.59 ms -2.7%
LongDictionaryBenchmark.Dictionary_Lookup(ItemCount: 100000) 1.59 ms 10.03 μs 1.60 ms -0.1%
RobinHoodDictionaryBenchmark.Dictionary_Lookup(ItemCount: 100000) 1.61 ms 8.88 μs 1.55 ms +3.8%
CelerityDictionaryBenchmark.CelerityDictionary_Lookup(ItemCount: 100000) 698.85 μs 2.55 μs 692.76 μs +0.9%
CelerityMultiMapBenchmark.CelerityMultiMap_Lookup(ItemCount: 100000) 731.58 μs 3.91 μs 732.53 μs -0.1%
FrozenCelerityDictionaryBenchmark.FrozenCelerityDictionary_Lookup(ItemCount: 100000) 7.41 ms 128.85 μs 7.65 ms -3.1%
IntDictionaryBenchmark.IntDictionary_Lookup(ItemCount: 100000) 675.39 μs 2.19 μs 672.87 μs +0.4%
LongDictionaryBenchmark.LongDictionary_Lookup(ItemCount: 100000) 701.50 μs 3.08 μs 703.06 μs -0.2%
RobinHoodDictionaryBenchmark.RobinHoodDictionary_Lookup(ItemCount: 100000) 830.95 μs 6.11 μs 835.14 μs -0.5%
SmallDictionaryBenchmark.Dictionary_Remove(ItemCount: 8) 1.73 μs 279.7 ns 1.31 μs +32.6% ⚠️
SmallDictionaryBenchmark.SmallDictionary_Remove(ItemCount: 8) 1.22 μs 59.9 ns 1.26 μs -3.2%
SmallDictionaryBenchmark.Dictionary_Remove(ItemCount: 64) 5.37 μs 818.7 ns 4.84 μs +10.8%
SmallDictionaryBenchmark.SmallDictionary_Remove(ItemCount: 64) 21.66 μs 579.9 ns 25.72 μs -15.8%
CelerityDictionaryBenchmark.Dictionary_Remove(ItemCount: 1000) 80.50 μs 6.81 μs 86.67 μs -7.1%
CelerityMultiMapBenchmark.Dictionary_Remove(ItemCount: 1000) 27.11 μs 1.31 μs 29.08 μs -6.8%
CeleritySetBenchmark.HashSet_Remove(ItemCount: 1000) 26.43 μs 1.56 μs 29.31 μs -9.8%
IntDictionaryBenchmark.Dictionary_Remove(ItemCount: 1000) 79.97 μs 7.24 μs 83.89 μs -4.7%
IntSetBenchmark.HashSet_Remove(ItemCount: 1000) 28.26 μs 4.14 μs 26.14 μs +8.1%
LongDictionaryBenchmark.Dictionary_Remove(ItemCount: 1000) 83.02 μs 7.89 μs 82.09 μs +1.1%
LongSetBenchmark.HashSet_Remove(ItemCount: 1000) 74.88 μs 5.22 μs 76.00 μs -1.5%
RobinHoodDictionaryBenchmark.Dictionary_Remove(ItemCount: 1000) 84.42 μs 9.63 μs 83.27 μs +1.4%
CelerityDictionaryBenchmark.CelerityDictionary_Remove(ItemCount: 1000) 145.91 μs 10.97 μs 146.96 μs -0.7%
CelerityMultiMapBenchmark.CelerityMultiMap_Remove(ItemCount: 1000) 142.97 μs 7.94 μs 150.04 μs -4.7%
CeleritySetBenchmark.CeleritySet_Remove(ItemCount: 1000) 135.31 μs 8.98 μs 147.25 μs -8.1%
IntDictionaryBenchmark.IntDictionary_Remove(ItemCount: 1000) 90.75 μs 7.63 μs 89.70 μs +1.2%
IntSetBenchmark.IntSet_Remove(ItemCount: 1000) 87.97 μs 7.49 μs 86.69 μs +1.5%
LongDictionaryBenchmark.LongDictionary_Remove(ItemCount: 1000) 87.50 μs 5.09 μs 89.84 μs -2.6%
LongSetBenchmark.LongSet_Remove(ItemCount: 1000) 81.04 μs 4.58 μs 87.58 μs -7.5%
RobinHoodDictionaryBenchmark.RobinHoodDictionary_Remove(ItemCount: 1000) 132.13 μs 3.85 μs 132.57 μs -0.3%
CelerityDictionaryBenchmark.Dictionary_Remove(ItemCount: 100000) 2.19 ms 263.36 μs 2.04 ms +7.7%
CelerityMultiMapBenchmark.Dictionary_Remove(ItemCount: 100000) 2.16 ms 241.45 μs 2.62 ms -17.4%
CeleritySetBenchmark.HashSet_Remove(ItemCount: 100000) 1.69 ms 16.15 μs 1.70 ms -0.4%
IntDictionaryBenchmark.Dictionary_Remove(ItemCount: 100000) 2.04 ms 9.64 μs 2.06 ms -0.9%
IntSetBenchmark.HashSet_Remove(ItemCount: 100000) 1.71 ms 10.96 μs 1.72 ms -0.4%
LongDictionaryBenchmark.Dictionary_Remove(ItemCount: 100000) 1.99 ms 26.08 μs 1.99 ms -0.3%
LongSetBenchmark.HashSet_Remove(ItemCount: 100000) 1.96 ms 16.72 μs 1.96 ms +0.0%
RobinHoodDictionaryBenchmark.Dictionary_Remove(ItemCount: 100000) 2.11 ms 61.80 μs 2.05 ms +3.0%
CelerityDictionaryBenchmark.CelerityDictionary_Remove(ItemCount: 100000) 1.57 ms 16.12 μs 1.58 ms -0.5%
CelerityMultiMapBenchmark.CelerityMultiMap_Remove(ItemCount: 100000) 1.87 ms 155.68 μs 2.50 ms -25.1% ✅
CeleritySetBenchmark.CeleritySet_Remove(ItemCount: 100000) 1.30 ms 54.45 μs 1.32 ms -1.5%
IntDictionaryBenchmark.IntDictionary_Remove(ItemCount: 100000) 3.01 ms 2.27 ms 1.62 ms +86.0%
IntSetBenchmark.IntSet_Remove(ItemCount: 100000) 1.34 ms 16.75 μs 1.33 ms +0.7%
LongDictionaryBenchmark.LongDictionary_Remove(ItemCount: 100000) 1.61 ms 64.12 μs 1.65 ms -2.3%
LongSetBenchmark.LongSet_Remove(ItemCount: 100000) 1.42 ms 19.29 μs 1.43 ms -0.5%
RobinHoodDictionaryBenchmark.RobinHoodDictionary_Remove(ItemCount: 100000) 1.89 ms 215.77 μs 1.70 ms +11.7%
Hashers (90)
Benchmark This PR StdDev main Δ
IntegerHasherBenchmark.Guid_Bcl 1.27 μs 2.3 ns 1.27 μs -0.2%
IntegerHasherBenchmark.Guid_Celerity 8.02 μs 11.8 ns 8.02 μs +0.0%
StringHasherBenchmark.Bcl_GetHashCode(Shape: ShortAscii) 15.37 μs 6.5 ns 15.39 μs -0.1%
StringHasherBenchmark.Djb2(Shape: ShortAscii) 22.02 μs 21.9 ns 22.00 μs +0.1%
StringHasherBenchmark.Djb2A(Shape: ShortAscii) 22.01 μs 27.8 ns 22.00 μs +0.0%
StringHasherBenchmark.Sdbm(Shape: ShortAscii) 32.82 μs 19.3 ns 32.82 μs +0.0%
StringHasherBenchmark.Elf(Shape: ShortAscii) 44.06 μs 198.8 ns 44.02 μs +0.1%
StringHasherBenchmark.Crc32(Shape: ShortAscii) 49.84 μs 37.0 ns 49.86 μs -0.0%
StringHasherBenchmark.Adler32(Shape: ShortAscii) 96.20 μs 45.2 ns 96.20 μs +0.0%
StringHasherBenchmark.FnV1(Shape: ShortAscii) 25.33 μs 16.4 ns 25.34 μs -0.0%
StringHasherBenchmark.FnV1_64(Shape: ShortAscii) 25.47 μs 25.6 ns 25.43 μs +0.2%
StringHasherBenchmark.FnV1A(Shape: ShortAscii) 10.63 μs 14.1 ns 10.63 μs +0.0%
StringHasherBenchmark.FnV1A_Full(Shape: ShortAscii) 22.83 μs 14.6 ns 22.83 μs -0.0%
StringHasherBenchmark.FnV1A_64(Shape: ShortAscii) 25.68 μs 19.7 ns 25.71 μs -0.1%
StringHasherBenchmark.JenkinsOaat(Shape: ShortAscii) 39.26 μs 24.2 ns 39.25 μs +0.0%
StringHasherBenchmark.Murmur2(Shape: ShortAscii) 15.18 μs 48.8 ns 15.18 μs -0.0%
StringHasherBenchmark.Murmur3(Shape: ShortAscii) 17.34 μs 61.2 ns 17.31 μs +0.2%
StringHasherBenchmark.XxHash32(Shape: ShortAscii) 16.90 μs 14.2 ns 16.90 μs +0.0%
StringHasherBenchmark.XxHash64(Shape: ShortAscii) 18.09 μs 10.0 ns 18.11 μs -0.1%
StringHasherBenchmark.XxHash3(Shape: ShortAscii) 14.84 μs 18.6 ns 14.87 μs -0.2%
StringHasherBenchmark.CityHash64(Shape: ShortAscii) 15.23 μs 13.6 ns 15.22 μs +0.0%
StringHasherBenchmark.MetroHash64(Shape: ShortAscii) 15.00 μs 13.3 ns 14.99 μs +0.0%
StringHasherBenchmark.SipHash13(Shape: ShortAscii) 27.34 μs 15.8 ns 27.36 μs -0.1%
StringHasherBenchmark.SipHash24(Shape: ShortAscii) 38.47 μs 15.3 ns 38.44 μs +0.1%
StringHasherBenchmark.HalfSipHash24(Shape: ShortAscii) 49.99 μs 62.0 ns 50.00 μs -0.0%
StringHasherBenchmark.HighwayHash64(Shape: ShortAscii) 479.39 μs 460.6 ns 478.98 μs +0.1%
StringHasherBenchmark.Bcl_GetHashCode(Shape: LongAscii) 96.83 μs 56.0 ns 96.85 μs -0.0%
StringHasherBenchmark.Djb2(Shape: LongAscii) 229.19 μs 147.9 ns 229.25 μs -0.0%
StringHasherBenchmark.Djb2A(Shape: LongAscii) 229.12 μs 71.3 ns 229.32 μs -0.1%
StringHasherBenchmark.Sdbm(Shape: LongAscii) 307.86 μs 95.8 ns 307.99 μs -0.0%
StringHasherBenchmark.Elf(Shape: LongAscii) 582.01 μs 4.87 μs 577.40 μs +0.8%
StringHasherBenchmark.Crc32(Shape: LongAscii) 589.29 μs 328.2 ns 589.31 μs -0.0%
StringHasherBenchmark.Adler32(Shape: LongAscii) 788.76 μs 236.1 ns 788.92 μs -0.0%
StringHasherBenchmark.FnV1(Shape: LongAscii) 289.35 μs 113.2 ns 289.44 μs -0.0%
StringHasherBenchmark.FnV1_64(Shape: LongAscii) 284.27 μs 64.5 ns 284.28 μs -0.0%
StringHasherBenchmark.FnV1A(Shape: LongAscii) 122.33 μs 81.5 ns 122.22 μs +0.1%
StringHasherBenchmark.FnV1A_Full(Shape: LongAscii) 281.57 μs 115.6 ns 281.56 μs +0.0%
StringHasherBenchmark.FnV1A_64(Shape: LongAscii) 286.63 μs 63.0 ns 286.79 μs -0.1%
StringHasherBenchmark.JenkinsOaat(Shape: LongAscii) 378.32 μs 135.1 ns 379.31 μs -0.3%
StringHasherBenchmark.Murmur2(Shape: LongAscii) 102.07 μs 64.7 ns 101.92 μs +0.1%
StringHasherBenchmark.Murmur3(Shape: LongAscii) 111.49 μs 1.58 μs 111.35 μs +0.1%
StringHasherBenchmark.XxHash32(Shape: LongAscii) 73.14 μs 46.0 ns 73.18 μs -0.0%
StringHasherBenchmark.XxHash64(Shape: LongAscii) 90.59 μs 933.7 ns 90.25 μs +0.4%
StringHasherBenchmark.XxHash3(Shape: LongAscii) 69.80 μs 86.5 ns 69.94 μs -0.2%
StringHasherBenchmark.CityHash64(Shape: LongAscii) 123.45 μs 1.00 μs 122.83 μs +0.5%
StringHasherBenchmark.MetroHash64(Shape: LongAscii) 77.14 μs 2.49 μs 77.12 μs +0.0%
StringHasherBenchmark.SipHash13(Shape: LongAscii) 114.55 μs 59.7 ns 114.64 μs -0.1%
StringHasherBenchmark.SipHash24(Shape: LongAscii) 161.10 μs 1.03 μs 161.04 μs +0.0%
StringHasherBenchmark.HalfSipHash24(Shape: LongAscii) 247.34 μs 385.6 ns 247.26 μs +0.0%
StringHasherBenchmark.HighwayHash64(Shape: LongAscii) 841.32 μs 8.36 μs 825.61 μs +1.9%
StringHasherBenchmark.Bcl_GetHashCode(Shape: NonAscii) 22.88 μs 9.5 ns 22.86 μs +0.1%
StringHasherBenchmark.Djb2(Shape: NonAscii) 43.39 μs 26.9 ns 43.39 μs -0.0%
StringHasherBenchmark.Djb2A(Shape: NonAscii) 43.44 μs 62.8 ns 43.39 μs +0.1%
StringHasherBenchmark.Sdbm(Shape: NonAscii) 61.56 μs 36.5 ns 61.54 μs +0.0%
StringHasherBenchmark.Elf(Shape: NonAscii) 99.73 μs 147.9 ns 99.70 μs +0.0%
StringHasherBenchmark.Crc32(Shape: NonAscii) 102.60 μs 55.7 ns 102.63 μs -0.0%
StringHasherBenchmark.Adler32(Shape: NonAscii) 175.35 μs 86.6 ns 175.39 μs -0.0%
StringHasherBenchmark.FnV1(Shape: NonAscii) 47.61 μs 32.8 ns 47.61 μs +0.0%
StringHasherBenchmark.FnV1_64(Shape: NonAscii) 50.03 μs 59.6 ns 50.04 μs -0.0%
StringHasherBenchmark.FnV1A(Shape: NonAscii) 21.66 μs 34.8 ns 21.64 μs +0.1%
StringHasherBenchmark.FnV1A_Full(Shape: NonAscii) 45.06 μs 39.9 ns 45.09 μs -0.1%
StringHasherBenchmark.FnV1A_64(Shape: NonAscii) 49.63 μs 52.5 ns 49.59 μs +0.1%
StringHasherBenchmark.JenkinsOaat(Shape: NonAscii) 73.12 μs 58.6 ns 73.13 μs -0.0%
StringHasherBenchmark.Murmur2(Shape: NonAscii) 27.18 μs 218.4 ns 27.28 μs -0.4%
StringHasherBenchmark.Murmur3(Shape: NonAscii) 31.86 μs 79.0 ns 31.90 μs -0.1%
StringHasherBenchmark.XxHash32(Shape: NonAscii) 23.91 μs 19.5 ns 24.01 μs -0.5%
StringHasherBenchmark.XxHash64(Shape: NonAscii) 29.14 μs 34.0 ns 29.11 μs +0.1%
StringHasherBenchmark.XxHash3(Shape: NonAscii) 22.89 μs 44.0 ns 23.01 μs -0.5%
StringHasherBenchmark.CityHash64(Shape: NonAscii) 22.01 μs 20.5 ns 22.01 μs +0.0%
StringHasherBenchmark.MetroHash64(Shape: NonAscii) 25.62 μs 26.3 ns 25.63 μs -0.0%
StringHasherBenchmark.SipHash13(Shape: NonAscii) 36.94 μs 78.8 ns 36.91 μs +0.1%
StringHasherBenchmark.SipHash24(Shape: NonAscii) 51.71 μs 604.4 ns 51.46 μs +0.5%
StringHasherBenchmark.HalfSipHash24(Shape: NonAscii) 73.33 μs 571.4 ns 73.24 μs +0.1%
StringHasherBenchmark.HighwayHash64(Shape: NonAscii) 512.80 μs 835.1 ns 513.57 μs -0.2%
IntegerHasherBenchmark.Int32_Bcl 686.9 ns 3.9 ns 686.8 ns +0.0%
IntegerHasherBenchmark.Int32_WangNaive 1.25 μs 0.6 ns 1.25 μs -0.0%
IntegerHasherBenchmark.Int32_Wang 3.02 μs 3.1 ns 3.02 μs -0.1%
IntegerHasherBenchmark.Int32_Murmur3 2.64 μs 2.2 ns 2.65 μs -0.1%
IntegerHasherBenchmark.Int64_Bcl 1.27 μs 1.2 ns 1.27 μs -0.0%
IntegerHasherBenchmark.Int64_WangNaive 1.87 μs 1.2 ns 1.87 μs +0.0%
IntegerHasherBenchmark.Int64_Wang 4.16 μs 2.1 ns 4.16 μs -0.0%
IntegerHasherBenchmark.Int64_Murmur3 2.37 μs 1.2 ns 2.37 μs +0.0%
IntegerHasherBenchmark.UInt32_Bcl 689.7 ns 7.1 ns 686.0 ns +0.5%
IntegerHasherBenchmark.UInt32_Default 1.25 μs 0.4 ns 1.25 μs +0.0%
IntegerHasherBenchmark.UInt32_Wang 3.02 μs 4.8 ns 3.02 μs +0.0%
IntegerHasherBenchmark.UInt32_Murmur3 2.64 μs 1.9 ns 2.64 μs +0.0%
IntegerHasherBenchmark.UInt64_Bcl 1.27 μs 1.0 ns 1.27 μs -0.1%
IntegerHasherBenchmark.UInt64_Default 2.37 μs 0.8 ns 2.37 μs -0.1%
IntegerHasherBenchmark.UInt64_Wang 4.16 μs 4.3 ns 4.16 μs +0.1%
IntegerHasherBenchmark.UInt64_WangNaive 1.87 μs 2.1 ns 1.87 μs +0.0%

Same-runner A/B: main (360c1d2) and this PR were built and benchmarked back-to-back on the same runner, so hardware variance cancels out. ⚠️ = PR mean ≥ +10% slower than main and beyond combined std-dev; ✅ = correspondingly faster.

@marius-bughiu
marius-bughiu deleted the feat/issue-22-frozen-celerity-set branch July 24, 2026 22:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant