Back to Home

SearchValues and FrozenCollections in .NET 8 for optimization

The article breaks down SearchValues and FrozenCollections in .NET 8 for speeding up search in hot paths. Benchmarks are provided (13x on strings, 2x on dictionaries) and code examples. Recommendations for use in read-heavy scenarios.

13x search acceleration in .NET 8 with SearchValues
Advertisement 728x90

.NET 8 Search Optimization: SearchValues and FrozenCollections for Hot Paths

.NET 8 introduces SearchValues<T> and collections like FrozenSet<T>/FrozenDictionary<TKey, TValue>, which slash overhead in hot paths. These structures do the heavy lifting during creation, delivering constant-time lookups without repeated analysis. They're perfect for high-throughput apps handling millions of operations.

Boosting Character Search with SearchValues

The standard string.IndexOfAny re-analyzes the character set on every call, piling up overhead in loops. SearchValues<char> is created once and caches the optimal search strategy.

During initialization, .NET performs:

Google AdInline article slot
  • Analysis of the input set (single char, range, or multi-char).
  • Algorithm selection with SIMD support (AVX2, SSE2).
  • Building data structures (bitmasks, hash tables).
SearchValues<char> separators = SearchValues.Create(".,;!?");
ReadOnlySpan<char> input = "Hello, world!";
int index = input.IndexOfAny(separators);

Search strategies vary by data:

  • Single character — vectorized check.
  • Range (0-9A-Z) — O(1) bitmask.
  • Small set (≤4 chars) — SIMD.
  • Large set — optimized hash table.

Benchmarks show massive gains:

| Scenario | IndexOfAny | SearchValues | Speedup |

Google AdInline article slot

|----------|------------|--------------|---------|

| Short string | ~5 ns | ~5 ns | 1x |

| Long string (2000 chars) | ~52 ns | ~4 ns | ~13x |

Google AdInline article slot

The biggest wins come with strings over 100 characters, skipping repeated analysis.

FrozenCollections: Immutable Hash Tables

HashSet<T> and Dictionary<TKey, TValue> carry mutation overhead like rehashing and bounds checks. FrozenSet<T> and FrozenDictionary<TKey, TValue> are tuned for read-only use cases.

During creation:

  • Key analysis for a perfect hash function with zero collisions.
  • Cache-friendly data layout.
  • Stripped runtime immutability checks.

Small collections use bitmasks instead of hashing.

var dict = Enumerable.Range(0, 100000)
    .ToFrozenDictionary(x => x, x => $"Value {{x}}");

if (dict.TryGetValue(42, out string? value))
{
    Console.WriteLine(value);
}

Benchmarks (100,000 elements):

| Operation | Dictionary | HashSet | FrozenDictionary | FrozenSet |

|-----------|------------|---------|------------------|-----------|

| TryGetValue/Contains | 3.78 ns | 4.47 ns | 1.87 ns | 3.37 ns |

FrozenDictionary is twice as fast as a standard Dictionary.

Real-World Use Cases

SearchValues:

  • Millions of lookups against fixed sets.
  • Log parsing, tokenization, validation.
  • Long strings (>100 characters).

FrozenCollections:

  • Static configs loaded at startup.
  • Lookup tables (error codes, statuses).
  • Read-heavy workloads with thousands of lookups.

Skip them for frequent mutations or rare calls—the creation cost outweighs benefits.

Practical Implementation

Cache at the app level:

public static class AppCache
{
    public static readonly SearchValues<char> UrlSafeChars = 
        SearchValues.Create("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~");
    
    public static readonly FrozenSet<string> ValidCurrencies = 
        new[] { "USD", "EUR", "GBP", "JPY", "CNY" }.ToFrozenSet();
}

Validation examples:

  • Email: SearchValues<char> invalidEmailChars = SearchValues.Create("()<>[]:;@\\,?\"");
  • TLD: FrozenSet<string> validTlds = LoadTldsFromConfig().ToFrozenSet();

Always profile with BenchmarkDotNet—payoff depends on operation volume.

Key Takeaways

  • SearchValues speeds up searches 13x on long strings via one-time optimization.
  • FrozenDictionary doubles TryGetValue performance vs. Dictionary.
  • Both shine with immutable sets in hot paths.
  • Profile first: useless for infrequent calls.
  • Leverage SIMD and perfect hashing for mid/senior dev wins.

— Editorial Team

Advertisement 728x90

Read Next