The new SearchValues class takes advantage of vectorization and hardware acceleration to speed up repeated searches in .NET 8.
With the release of .NET 8, Microsoft introduced a plethora of new features and enhancements in the .NET Core and ASP.NET Core frameworks. One such feature is the SearchValues class, which marks a significant step forward in efficiently fetching data from data sets.
SearchValues is a new type introduced in .NET 8 designed to improve application performance. By using optimization techniques like vectorization and hardware acceleration, SearchValues delivers speed enhancements while seamlessly blending with .NET Core and ASP.NET Core.
In this article, we’ll explain how you can use SearchValues to improve the speed of searches in .NET Core applications.
Create a console application project in Visual Studio
First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2022 is installed in your system, follow the steps outlined below to create a new .NET Core console application project.
- Launch the Visual Studio IDE.
- Click on “Create new project.”
- In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed.
- Click Next.
- In the “Configure your new project” window, specify the name and location for the new project.
- Click Next.
- In the “Additional information” window shown next, choose “.NET 8.0 (Long Term Support)” as the framework version you would like to use.
- Click Create.
We’ll use this .NET 8 console application project to work with the code examples shown in the subsequent sections of this article.
String search performance gotchas
String searches are a fundamental part of many applications. While there are several ways to search strings, finding the most efficient method in each case can be a challenge. For example, while you can use the IndexOfAny() method to search for the first occurrence of a character in a string, it may not be the optimal method for subsequent searches where the input data differs.
Consider the following code that illustrates how you can perform a search on a string using the IndexOfAny() method.
string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
char[] c = {'s'};
int indexOfAlpha = str.IndexOfAny(c);
If you next want to search for more than one character in the same string, you will need a different search strategy, as shown in the code snippet given below.
string numeric = "0123456789";
int indexOfAlphaNumeric = str.IndexOfAny(numeric.ToCharArray());
Console.WriteLine(indexOfAlpha);
Console.WriteLine(indexOfAlphaNumeric);
Note that we’ve converted the string numeric into an array of characters because the IndexOfAny() method requires a character array as a parameter. If we want to search for a range of characters, we must use a string or a char[].
SearchValues makes these different kinds of searches both more straightforward to code and faster to execute. That’s because SearchValues takes advantage of the vector processing support in today’s CPUs that can process multiple values in parallel.
What is SearchValues?
SearchValues is a new type in the System.Buffers namespace in .NET that employs vectorization and hardware acceleration to improve search efficiency and performance. The SearchValues class represents an immutable and read-only collection of values.
You can use SearchValues on any ReadOnlySpan, which implies that you can use it for searching not only strings but also for values in a collection or even values in a block of memory. SearchValues instances are designed explicitly for situations where the same set of values are used frequently for searching at run time.
Consider the following code.
SearchValues<char> searchValues = SearchValues.Create("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
bool IsAlphanumericContent(ReadOnlySpan<char> text) =>
text.IndexOfAny(searchValues) != -1 ? true:false;
You can invoke the IsAlphanumericContent() method to verify if the parameter contains alphanumeric text using the following code.
Console.WriteLine(IsAlphanumericContent("This is an alphanumeric text for demonstration purposes 0nly."));
Note that the SearchValues type is designed to search for the first occurrence of a particular value inside a collection.
When you use SearchValues in your code, the runtime determines an optimal implementation for the particular scenario. For example, in the case of the following line of code, SearchValues will search for a contiguous range of values and determine an optimal implementation for that use case.
SearchValues<char>.Create("12345");
The performance gains you achieve with SearchValues will depend on your input data and the type and volume of the data being searched. You can expect more significant performance benefits as the volume of data grows.
Benchmarking search performance in .NET
The following code shows how you can run benchmark tests to compare the performance of the IndexOfAny method using a character array versus using SearchValues.
[MemoryDiagnoser]
public class PerformanceBenchmark
{
private char[]
charValues = { 's', 'a', 'm', 'p', 'l', 'e' };
private SearchValues<char> searchValues =
SearchValues.Create("sample");
private string text =
"this text is in lower case for testing purposes only";
[Benchmark]
public int IndexOfCharBenchmark()
{
return text.AsSpan().IndexOfAny(charValues);
}
[Benchmark]
public int IndexOfSearchValuesBenchmark()
{
return text.AsSpan().IndexOfAny(searchValues);
}
}
To run the above benchmark tests, you should specify the following code in your Program.cs.
using BenchmarkDotNet.Running;
using SearchValuesDemo;
var summary = BenchmarkRunner.Run<PerformanceBenchmark>();
Figure 1 shows the performance differences of these two approaches when you run the benchmarks at the console window.
As you can see from the benchmark data, there is a considerable performance gain when you use SearchValues.
The introduction of SearchValues in .NET 8 marks a paradigm shift in the efficiency of searching for values in a collection of data. System.Buffers.SearchValues is a new type designed to be efficient when a collection of values is frequently used for searches during run time. To enhance the efficiency of the search process, SearchValues precomputes all of the necessary data when an instance is instantiated.