High-performance .NET collections that beat the BCL on documented workloads.
Celerity ships specialised dictionaries and sets with struct-based hashers, zero-cost generic dispatch, and continuous performance tracking on every commit. If a type doesn't outperform its System.Collections.Generic counterpart on a real benchmark, it doesn't ship.
dotnet add package Celerity.Collections
Latest measurement vs .NET BCL
Lookup
—
vs Dictionary / HashSet
Insert
—
vs Dictionary / HashSet
Remove
—
vs Dictionary / HashSet
What ships in the box
IntDictionary<TValue>int-keyed dictionary, default Wang hash.
LongDictionary<TValue>long-keyed dictionary, default Wang hash.
CelerityDictionary<K, V, H>Generic dictionary with a struct hasher constraint.
RobinHoodDictionary<K, V, H>Robin Hood open addressing: bounded probe variance for clustered keys.
SwissDictionary<K, V, H>Swiss-table SIMD group probing: one Vector128 compare tests 16 slots per lookup.
HashCachingDictionary<K, V, H>Struct-of-arrays layout with a cached-fingerprint side array: probes scan metadata only and short-circuit expensive key equality.
PooledCelerityDictionary<K, V, H>ArrayPool-backed, disposable dictionary that recycles its buffers to cut GC pressure.
FrozenCelerityDictionary<V>Build-once string-keyed dictionary with perfect hashing for single-probe lookups.
CelerityMultiMap<K, V, H>One-to-many map: each key groups multiple values. Implements
ILookup.CelerityMultiSet<T, H>Counting multiset (bag): each element maps to its multiplicity. Single-probe
Add for frequency counting.SmallDictionary<K, V>Flat-array, linear-scan dictionary tuned for the very-small (n ≤ ~16) case.
EnumMap<TEnum, V>Dense array-backed dictionary for enum keys (the .NET EnumMap): a lookup is a direct array index — no hashing, no probing, no collisions. Ascending-value enumeration. The dictionary counterpart of EnumSet.
IntSetint-keyed set, default Wang hash.
LongSetlong-keyed set, default Wang hash.
CeleritySet<T, H>Generic set with a struct hasher constraint.
SwissSet<T, H>Swiss-table SIMD group probing for sets: one Vector128 compare tests 16 slots per membership check, so negative lookups stay cheap. The set counterpart of SwissDictionary.
RobinHoodSet<T, H>Robin Hood open addressing for sets: bounded probe variance and early-exit negative lookups on clustered elements. The set counterpart of RobinHoodDictionary.
HashCachingSet<T, H>Struct-of-arrays layout with a cached-fingerprint side array for sets: probes scan metadata only and short-circuit expensive element equality. The set counterpart of HashCachingDictionary.
PooledCeleritySet<T, H>ArrayPool-backed, disposable set that rents its backing array and returns it on Dispose, recycling buffers to cut GC pressure on short-lived, frequently-rebuilt sets. The set counterpart of PooledCelerityDictionary.
FrozenCeleritySet<H>Build-once string set with perfect hashing for single-probe membership tests. Implements
IReadOnlySet.SmallSet<T>Flat-array, linear-scan set tuned for the very-small (n ≤ ~16) case. No hasher; the default element is stored inline. The set counterpart of SmallDictionary.
EnumSet<TEnum>Bit-vector set for enum keys (the .NET EnumSet): membership is a single bit test and set algebra is word-wise bitwise ops — no hashing, no boxing. Ascending-value enumeration.
SparseSetBounded-universe integer set (Briggs–Torczon sparse set): O(1) Clear that leaves the backing arrays untouched and dense, cache-friendly iteration — for clear-and-rebuild "visited" sets in graph / ECS / sweep-line code.
BloomFilter<T, H>Probabilistic membership filter: bit-array storage, no false negatives, tunable false-positive rate. A fraction of a HashSet's memory.
CuckooFilter<T, H>Probabilistic membership filter that supports deletion: fingerprint buckets, no false negatives, tunable false-positive rate, ≤2 cache lines per lookup. The Bloom filter you can
Remove from.XorFilter<T, H>Probabilistic membership filter that is build-once & immutable: ~9.84 bits/element (smaller than a Bloom filter at the same rate), three probes + two XORs per lookup. The smallest, fastest-to-query filter for a fixed element set.
BitSetDense exact bit vector: O(n/64) population count and SIMD-accelerated bulk And/Or/Xor/Not over 64-bit words. A faster, count-aware BitArray.
HyperLogLog<T, H>Probabilistic cardinality estimator: counts distinct elements in a fixed few KB with ~0.8% error, no matter how many you add. Mergeable; a fraction of a HashSet's memory.
CountMinSketch<T, H>Probabilistic frequency estimator: estimates how often each element occurs from a fixed few KB, never underestimating. Mergeable; a fraction of a frequency-table Dictionary's memory.
TopKSketch<T, H>Top-k / heavy-hitters sketch (Space-Saving): reports a stream's most frequent elements from a fixed k monitors, never underestimating. O(k) memory instead of one entry per distinct key.
LruCache<K, V, H>Fixed-capacity least-recently-used cache with O(1) get/put and automatic eviction. Recency runs through an intrusive list over fixed arrays, so the hot get/put/evict path allocates nothing — unlike the idiomatic
Dictionary+LinkedList LRU that allocates a node per insert.Deque<T>Growable double-ended queue backed by a circular buffer: O(1) push/pop/peek at both ends and O(1) random access. The array-backed deque the BCL lacks — a bounded churn allocates nothing (vs a node per op for
LinkedList) and enumerates contiguous memory.DisjointSet<T>Union-find over arbitrary elements: partitions them into disjoint sets with near-O(1) amortized
Union/Find/Connected via union-by-size and path halving. The union-find the BCL lacks — incremental connectivity, connected components, and Kruskal MST in near-linear time, where a Dictionary+HashSet set-merge is quadratic.IndexedPriorityQueue<E, P, H>Addressable binary min-heap: unlike the BCL
PriorityQueue it can change a queued element's priority (Update / decrease-key) and remove an arbitrary element in O(log n), and answer Contains/TryGetPriority in O(1). The heap the priority-relaxation loop of Dijkstra / Prim / A* needs — no lazy-deletion heap growth.Trie<TValue>Ordered prefix tree mapping string keys to values:
GetByPrefix lists every entry under a prefix in O(prefix + matches) and TryGetLongestPrefix finds the longest stored prefix of a query in O(query). The trie the BCL lacks — autocomplete, routing, and ordered iteration, where a Dictionary must scan every key and run StartsWith.FenwickTree<T>Binary Indexed Tree over a fixed-length numeric sequence: point update and prefix / range sum both in O(log n), in one array with no per-node overhead. The prefix-sum structure the BCL lacks — running aggregates, rank counters and cumulative-frequency tables, where a plain array is O(n) per query or O(n) per update.
BTreeDictionary<K, V, C>Sorted map backed by a B-tree with up to 31 keys per node in flat arrays, so a lookup visits ~log32(n) nodes instead of chasing ~log2(n) pointers. The B-tree the BCL lacks —
Min/Max, lower/upper bound and O(log n + k) range scans, where SortedDictionary is a red-black tree with an object per entry and SortedList memmoves on every insert.BTreeSet<T, C>The set counterpart: ordered elements packed 31 to a node, with the same ordered surface and an in-order walk over contiguous arrays instead of successor pointers. Beats
SortedSet on the interleaved insert + membership + range-scan workload, and stores no values, so the memory saving is larger still.Celerity.HashingWang, Murmur3, FNV-1a, Guid, default fallback.
Built with Celerity
Celerity.RingConsistent-hash & rendezvous (HRW) rings for sharding and request routing. Byte-identical node assignment across OS, architecture, and runtime (x64, arm64, Blazor WASM), so every node in a cluster agrees on the mapping. Fills a gap the BCL has no type for. Separate NuGet package.
Celerity.SentinelStreaming abuse / heavy-hitter detection: top offenders, per-key rate, and fan-out cardinality of a request stream in a fixed footprint regardless of key cardinality, so it survives the attacker key-rotation that OOMs a
Dictionary<,> counter. Separate NuGet package.Celerity.CardinalityMergeable approximate
COUNT(DISTINCT) and windowed dedup over unbounded streams: exact for small inputs, promoting to a fixed ~16 KB estimator, with deterministic cross-shard merge identical on every runtime. Separate NuGet package.Design notes
Hashers are structs passed as generic constraints (
where THasher : struct, IHashProvider<T>) so the JIT devirtualizes and inlines Hash() on the probe path. Dictionaries implement IReadOnlyDictionary<TKey, TValue>, ship allocation-free struct enumerators, and handle default(TKey) out-of-band so the zero / null key never collides with the empty-slot sentinel. Celerity is single-threaded and does not guarantee iteration order — if you need either, use ConcurrentDictionary<,> or stay on the BCL.