SearchValues
Why SearchValues appeared
.NET 8 introduced SearchValues
Benchmarks that matter
The tests that sparked the discussion used a 32 MB buffer containing five delimiter characters. Three approaches were timed on .NET 10:
- IndexOfAny(char[]) – 10.2 ms
- Manual foreach loop over the span – 19.4 ms
- SearchValues
– 9.7 ms
SearchValues beat the built-in IndexOfAny by only half a millisecond. The result isn’t a dramatic “five-times faster” claim that circulates on forums; modern .NET already optimizes IndexOfAny for small sets, narrowing the gap.
When delimiters are rare—appearing once every 40 KB instead of every 60 bytes—the picture changes:
- IndexOfAny(char[]) – 5.6 ms
- SearchValues
– 3.3 ms
Now SearchValues is 1.7 × faster because the engine sprints through long runs of non-matching data with vectorized steps, while IndexOfAny falls back to a less aggressive path.
The hidden cost
SearchValues isn’t free. Constructing the object allocates memory and builds internal lookup tables. If you instantiate it on every method call, the overhead dwarfs any runtime win. A benchmark that called the scanning routine one million times on short lines showed:
- Static (reused) SearchValues – 25.1 ms total
- Per-call SearchValues – 70.2 ms total
Creating the object per call makes the whole operation three times slower than not using SearchValues at all. Store the instance in a static readonly field or otherwise reuse it across calls.
When to reach for SearchValues
- Large inputs, few matches – The vectorized path shines when the scanner can skip over long stretches without hitting a delimiter.
- Repeated scans with the same set – If the same characters are searched for many times, the one-time setup pays off.
When to stay with IndexOfAny
- Short strings or many matches – The extra setup cost outweighs the marginal speed gain.
- Code that already uses IndexOfAny – Modern .NET’s implementation is already highly tuned for small character sets, so swapping in SearchValues may not move the needle.
Common pitfalls
- Embedding the construction in hot loops – Leads to the three-fold slowdown shown above.
- Trying to “out-smart” the runtime with manual loops – The manual foreach version was twice as slow as both built-in methods, confirming that the .NET libraries are hard to beat without deep knowledge of SIMD instructions.
- Assuming universal speedups – The half-millisecond win on dense data proves that SearchValues is not a universal cheat code; its benefits are data-dependent.
Takeaway
SearchValues
Source code and full test suite: https://dev.to/ssukhpinder/when-searchvalues-actually-pays-off-310l
