.NET 8 continues to enhance its collection of performance-oriented tools with the introduction of the SearchValues<T> class, found in the System.Buffers namespace.
What is SearchValues<T>?
SearchValues<T> is designed to provide an immutable, read-only set of values that is optimized for efficient searching. The class is particularly useful in scenarios where the same set of values is frequently searched at runtime. It supports creation from ReadOnlySpan<Byte> or ReadOnlySpan<Char> (in .NET 9 it will support strings as well), making it versatile for handling both binary and text data.
Key Features
- Immutability: Once a
SearchValues<T>instance is created, it cannot be modified, ensuring thread safety and consistency. - Optimized Search Operations: The internal structure is optimized for search operations, potentially offering better performance than traditional lists or arrays.
Examples
public class VowelChecker
{
private static readonly char[] Vowels = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'];
private static readonly SearchValues<char> vowelSearchValues = SearchValues.Create(Vowels);
public static bool AreAllCharactersVowels(string input)
{
return input.AsSpan().IndexOfAnyExcept(vowelSearchValues) == -1;
}
public static void TestVowels()
{
var testInput = "aeiouAEIOU";
var result = AreAllCharactersVowels(testInput); //true
Console.WriteLine($"Are all characters in '{testInput}' vowels? {result}");
testInput = "Hello World!";
result = AreAllCharactersVowels(testInput); //false
Console.WriteLine($"Are all characters in '{testInput}' vowels? {result}");
}
}
In the example above, we can check if a word only contains vowels. This is now super fast compared to checking one character at a time!
Let’s create a benchmark and see the results:
[MemoryDiagnoser]
public class SearchValuesContains
{
private const string Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
private static readonly char[] Base64CharactersArray = Base64Chars.ToCharArray();
private static readonly SearchValues<char> searchValues = SearchValues.Create(Base64CharactersArray);
private static readonly HashSet<char> Base64CharactersSet = new(Base64CharactersArray);
[Params("sDSFGDSFW#dFGvbnjUUi&", "sDSFGDSFW")]
public string Value { get; set; }
[Benchmark]
public bool IsBase64SearchValues()
{
return Value.AsSpan().IndexOfAnyExcept(searchValues) == -1;
}
[Benchmark]
public bool IsBase64Array()
{
return Value.AsSpan().IndexOfAnyExcept(Base64CharactersArray) == -1;
}
[Benchmark]
public bool IsBase64Loop()
{
foreach (var character in Value)
{
if (!Base64CharactersSet.Contains(character))
{
return false;
}
}
return true;
}
}
The results:
| Method | Value | Mean | Error | StdDev | Allocated |
|---|---|---|---|---|---|
| IsBase64SearchValues | sDSFGDSFW | 1.887 ns | 0.0600 ns | 0.0561 ns | – |
| IsBase64Array | sDSFGDSFW | 19.154 ns | 0.0427 ns | 0.0356 ns | – |
| IsBase64Loop | sDSFGDSFW | 29.725 ns | 0.1367 ns | 0.1067 ns | – |
| IsBase64SearchValues | sDSFG(…)jUUi& [21] | 2.460 ns | 0.0168 ns | 0.0157 ns | – |
| IsBase64Array | sDSFG(…)jUUi& [21] | 56.959 ns | 0.3779 ns | 0.3535 ns | – |
| IsBase64Loop | sDSFG(…)jUUi& [21] | 35.028 ns | 0.2251 ns | 0.1879 ns | – |
As we can see the new way to search is a lot faster! Always benchmark yourself and see the results for your own use cases!
Conclusion
The SearchValues<T> class is a powerful addition to the .NET 8 library, especially for applications where search operations are a bottleneck. By leveraging this new class, developers can reduce the computational overhead associated with frequent searches, leading to more efficient and faster applications.
Affiliate promo
If you love learning new stuff and want to support me, consider buying a course from Dometrain using this link: Browse courses – Dometrain. Thank you!
Leave a comment