.NET SearchValues: When it actually boosts performance
SearchValues<T> is often framed as a magic bullet for string scanning, but my recent benchmarks on a log parser show that the performance gains are highly situational. I decided to measure the actual impact on a 32MB buffer using .NET 10 in a Linux container to see where the overhead actually sits.The Performance Gap
SearchValues (introduced in .NET 8) creates a precomputed set of values to optimize scanning. In theory, it's faster than IndexOfAny. In practice, the results depend on match density.
When delimiters are dense (one every ~60 characters), the difference is negligible:
IndexOfAny(char[]) : 10.2 ms
SearchValues : 9.7 msHowever, when searching for rare characters (one every ~40 KB), the vectorized fast path actually has room to run:
IndexOfAny(char[]) : 5.6 ms
SearchValues : 3.3 msThat's a 1.7x speedup. The real takeaway here isn't that SearchValues is always better, but that hand-rolled foreach loops are the actual bottleneck. A manual loop checking characters was consistently 2x slower than both API options.
The Static Cache Requirement
The biggest "gotcha" is the instantiation cost. SearchValues is designed to be created once and reused. If you call .Create() inside a hot method, you're destroying your performance.
In a test with one million short lines:
- Static SearchValues: 25.1 ms
- Per-call SearchValues: 70.2 ms
If it isn't stored in a
static readonly field, you're essentially adding overhead for no reason.// Correct: Pay the setup cost once
static readonly SearchValues<char> Delimiters = SearchValues.Create(['=', '&', ';', '?', '#']);
// Incorrect: Avoid doing this in a loop/hot path
var delimiters = SearchValues.Create(['=', '&', ';', '?', '#']);Practical Implementation Advice
If you're looking for a deep dive into optimizing your AI workflow or data processing pipeline in .NET, follow these rules for scanning:
1. Rare matches + Large buffers: Use SearchValues<T> stored in a static field.
2. Dense matches or short strings: Standard IndexOfAny is usually sufficient.
3. Manual loops: Replace them immediately. Whether you use SearchValues or IndexOfAny, the built-in APIs crush manual character checks.
For those needing multi-substring searches, .NET 9 added support for that, which is a separate beast entirely.
Full runnable sample:
https://github.com/ssukhpinder/dev-to-code-samples/tree/main