.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:

MethodValueMeanErrorStdDevAllocated
IsBase64SearchValuessDSFGDSFW1.887 ns0.0600 ns0.0561 ns
IsBase64ArraysDSFGDSFW19.154 ns0.0427 ns0.0356 ns
IsBase64LoopsDSFGDSFW29.725 ns0.1367 ns0.1067 ns
IsBase64SearchValuessDSFG(…)jUUi& [21]2.460 ns0.0168 ns0.0157 ns
IsBase64ArraysDSFG(…)jUUi& [21]56.959 ns0.3779 ns0.3535 ns
IsBase64LoopsDSFG(…)jUUi& [21]35.028 ns0.2251 ns0.1879 ns
Benchmarked in .NET 8

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

Trending