Deep Dive into SearchValues<T> in .NET 8: When to Use and When to Avoid
Developer shares real benchmark results after using SearchValues<T>, revealing stark performance differences between Static setup and recreation on every call.

Stock photo for illustration only, not from the actual event
- SearchValues
introduced in .NET 8 stores a set of constants for searching and the runtime selects the fastest strategy. - Benchmark results for dense delimiters show almost no difference from the traditional IndexOfAny, but sparse matches are about 1.7 times faster.
- Creating SearchValues inside a method per call is nearly 3 times slower than storing it in a static readonly.
Many often view the new tool SearchValues<T> added in .NET 8 as a cheat code that makes string scanning up to five times faster, tempting an immediate refactoring of existing code. But for developers maintaining log management systems that primarily rely on IndexOfAny, performance testing before diving in is the first priority.
From comparative testing in cases where the log buffer has a frequency delimiter densely packed every 60 characters, the results are quite surprising as there is very little difference between the traditional method and the new tool:
The reason for this is that the IndexOfAny method on modern .NET with small character sets is already well-vectorized. On the other hand, writing a manual check loop the traditional way turns out to be twice as slow as other methods, reflecting that the real performance bottleneck usually comes from the custom loops we write ourselves rather than the standard API.
However, the situation changes when modifying the data pattern to have rarer delimiters, averaging once every 40 kilobytes, which is a simulation case for anomaly scanning. The test results clearly show a difference:
- IndexOfAny(char[]): Takes 5.6 milliseconds (found 723 matches)
- SearchValues<char>: Takes 3.3 milliseconds (found 723 matches)
In cases where the target hit rate is relatively low, SearchValues can perform at its full potential in sweeping across long spans of data with nothing in them using wider vectorized steps.
The concept behind SearchValues<T> is to shift the precomputation burden upfront so the runtime doesn't have to recalculate every time a search is performed. Understanding underlying architectural workings like vectorization allows developers to choose APIs that fit the data characteristics, whether dense or sparse patterns, significantly impacting overall application performance.
Another crucial point developers must be extremely careful about is the cost of object creation. If SearchValues is newly created inside a method every time it is called instead of being stored in a static readonly variable, test results with a million short lines of data paint a clear picture:
- Static SearchValues: Takes 25.1 milliseconds
- SearchValues per call: Takes 70.2 milliseconds
Creating a new object per single call drops performance by nearly three times, making it even slower than not using it at all. If not declared as a static variable, we are essentially writing code that slows down the system unnecessarily.

Illustration from stock photo library, not an actual photo from the event
Source: Dev.to
Found something wrong in this article? Report an issue with this article
Comments
Leave a Comment