When working with collections in .NET, we often rely on abstractions that make our lives easier, like List<T> or Dictionary<TKey, TValue>. While these abstractions are incredibly powerful, they sometimes introduce overhead, especially in performance-critical scenarios. If you are a crazy person like me and want to maximize performance, this class can help. Let me explain what it is and see some practical examples.

What is CollectionsMarshal?

The CollectionsMarshal class lives in the System.Runtime.InteropServices namespace and provides low-level access to the internals of some common collections. Its primary goal is to give developers tools to bypass the abstraction layers of collections for performance-sensitive scenarios.

For example, you can use it to:

  • Access the internal array of a List<T> without copying it.
  • Directly manipulate the internal storage of a collection.
  • Avoid reading twice from a Dictionary

Examples

Accessing a List as a Span: When you need to perform operations on a List<T>, accessing it as a Span<T> can reduce overhead, as you avoid bounds checking and iteration through the list’s abstraction layer.

public static void InternalArrayOfList()
{
    List<int> numbers = new(5) { 1, 2, 3, 4, 5 };

    // Access the internal array as a Span
    Span<int> span = CollectionsMarshal.AsSpan(numbers);

    // Modify the span, which directly modifies the list
    for (int i = 0; i < span.Length; i++)
    {
        span[i] *= 2;
    }

    Console.WriteLine(string.Join(", ", numbers)); // Output: 2, 4, 6, 8, 10
}

⚠️ Warning: CollectionsMarshal.AsSpan provides a view into the list’s internal array. This means modifying the span modifies the list itself. Be cautious, as changes could lead to unpredictable behavior if the list’s size changes.

Optimizing Dictionary Access: The CollectionsMarshal.GetValueRefOrAddDefault method provides a direct reference to the value, avoiding unnecessary allocations.

public static void GetReferenceFromDictionary()
{
    Dictionary<string, int> wordCounts = new();

    // Add a word or get its reference
    ref int count = ref CollectionsMarshal.GetValueRefOrAddDefault(wordCounts, "example", out bool exists);

    // Update the value directly
    count++;
    Console.WriteLine($"'example' count: {wordCounts["example"]}"); // Output: 'example' count: 1

}

Accessing Values by Reference in Dictionaries: Retrieves a reference to a value in the dictionary if the key exists; otherwise, returns a null reference. This allows for direct manipulation of the value without additional lookups.

public static void GetValueOrNullFromDictionary()
{
    Dictionary<string, int> wordCounts = new()
    {
        { "example", 1 }
    };

    // Access the value by reference
    ref int count = ref CollectionsMarshal.GetValueRefOrNullRef(wordCounts, "example");

    if (!Unsafe.IsNullRef(ref count))
    {
        count++;
        Console.WriteLine($"'example' count: {wordCounts["example"]}"); // Output: 'example' count: 2
    }
}

This method is particularly useful when you need to modify the value associated with a specific key directly, avoiding the overhead of multiple lookups without adding a default value like the example we saw before.

When to Use CollectionsMarshal

While CollectionsMarshal is great, I would use it mainly when:

  1. Performance is critical.
    • Tight loops or hot paths in your code could benefit from the reduced overhead.
  2. You have full control over the collection’s lifecycle.
    • Since these methods expose internal structures, improper usage can lead to bugs or unexpected behavior.

Conclusion

Summarizing, CollectionsMarshal is a powerful tool when performance matters and you need extra control over your collections. It’s not something you’d use every day, but in the right scenarios, like optimizing tight loops or avoiding unnecessary allocations, it can make a real difference. Just remember: with great power comes great debugging sessions if you’re not careful.


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