XenoAtom.Collections [](https://github.com/XenoAtom/XenoAtom.Collections/actions/workflows/ci.yml) [](https://www.nuget.org/packages/XenoAtom.Collections/)

October 27, 2024 ยท View on GitHub

This .NET library offers structure-based collections optimized for high performance and minimal memory usage. It features UnsafeList<T>, UnsafeDictionary<TKey, TValue>, and UnsafeHashSet<T>. These collections are implemented as value types backed by a managed array (except for fixed capacity list UnsafeList<T>.N2 ... UnsafeList<T>.N1024), specifically designed for performance-critical scenarios where avoiding additional memory allocations for container objects is essential.

The code has been derived from the .NET Core runtime released under the MIT license.

These collections contains unsafe methods (prefixed by Unsafe) and should be used with caution.

โœจ Features

  • Three main collections: UnsafeList<T>, UnsafeDictionary<TKey, TValue> and UnsafeHashSet<T>
    • Plus fixed capacity list for efficient stack allocation:
      • UnsafeList<T>.N2
      • UnsafeList<T>.N4
      • UnsafeList<T>.N8
      • UnsafeList<T>.N16
      • UnsafeList<T>.N32
      • UnsafeList<T>.N64
      • UnsafeList<T>.N128
      • UnsafeList<T>.N256
      • UnsafeList<T>.N512
      • UnsafeList<T>.N1024
  • Faster access for Dictionary and HashSet by requiring the key to be IEquatable<TKey>
  • Struct based collections to avoid an allocation for the container and improve locality
  • Proper debugger support for collections with custom DebuggerDisplay
  • A few advanced unsafe methods to avoid checks
    • e.g Unsafe<T>.UnsafeSetCount, Unsafe<T>.UnsafeGetRefAt...
  • A fast sort by ref via Span<T>.SortByRef that can inline the comparer and avoid the copy to the stack of the value being compared.
    • NativeAOT compatible
  • net8.0+ support

๐Ÿ“– Usage

In general, the API is similar to the standard collections.

It is recommended to not expose in public APIs these unsafe collections but instead use them internally for performance-critical scenarios.

Here are some examples

  • UnsafeList<T>
    // UnsafeList is a struct, but the underlying array is a managed array
    var list = new UnsafeList<int>();
    list.Add(1);
    list.Add(2);
    list.Add(3);
    list.Add(4);
    var span = list.AsSpan();
    foreach(var value in span)
    {
        Console.WriteLine(value);
    }
    
  • UnsafeList<T>.N4
    // UnsafeList is a struct, and the underlying array is a fixed size array (on the stack)
    var list = new UnsafeList<int>.N4();
    list.Add(1);
    list.Add(2);
    list.Add(3);
    list.Add(4);
    var span = list.AsSpan();
    foreach(var value in span)
    {
        Console.WriteLine(value);
    }
    
  • UnsafeDictionary<TKey, TValue>
    // UnsafeDictionary is a struct, but the underlying array is a managed array
    var dictionary = new UnsafeDictionary<int, string>();
    dictionary.Add(1, "One");
    dictionary.Add(2, "Two");
    dictionary.Add(3, "Three");
    dictionary.Add(4, "Four");
    foreach(var pair in dictionary)
    {
        Console.WriteLine($"{pair.Key} = {pair.Value}");
    }
    
  • UnsafeHashSet<T>
    // UnsafeDictionary is a struct, but the underlying array is a managed array
    var hashSet = new UnsafeHashSet<int>();
    hashSet.Add(1);
    hashSet.Add(2);
    hashSet.Add(3);
    hashSet.Add(4);
    foreach(var value in hashSet)
    {
        Console.WriteLine(value);
    }
    

๐Ÿ“Š Benchmarks

Some benchmarks are available in the src\XenoAtom.Collections.Bench folder. Here are some results:

  • UnsafeList<T> and UnsafeDictionary<TKey, TValue> can be up to 20% faster than the standard List<T> and Dictionary<TKey, TValue> for some scenarios.
  • UnsafeList<T>.N16 can be up to 50% faster than the standard List<T> for adding elements.
  • Span<T>.SortByRef can be up to 50% faster than Array.Sort/Span.Sort for some scenarios.
MethodMeanErrorStdDevRatio
UnsafeList<int>.Add4.807 ns0.0667 ns0.0624 ns0.79
UnsafeList<int>.N16.Add2.570 ns0.0172 ns0.0161 ns0.42
List<int>.Add6.090 ns0.0361 ns0.0338 ns1.00
MethodMeanErrorStdDevRatio
UnsafeDictionary<int, int>44.85 ns0.577 ns0.539 ns0.81
Dictionary<int, int>55.06 ns0.307 ns0.287 ns1.00
MethodParamSizeMeanErrorStdDevRatioGen0AllocatedAlloc Ratio
SortByRef36.468 ns0.0631 ns0.0590 ns0.43--0.00
Sort314.993 ns0.0419 ns0.0350 ns1.000.003864 B1.00
SortByRef830.392 ns0.1365 ns0.1277 ns0.75--0.00
Sort840.525 ns0.1555 ns0.1379 ns1.000.003864 B1.00
SortByRef1673.797 ns0.1232 ns0.1092 ns0.59--0.00
Sort16125.879 ns0.5184 ns0.4849 ns1.000.003864 B1.00
SortByRef256870.983 ns1.4083 ns1.0995 ns0.48--0.00
Sort2561,813.291 ns8.7028 ns8.1406 ns1.000.003864 B1.00

๐Ÿชช License

This software is released under the BSD-2-Clause license.

๐Ÿค— Author

Alexandre Mutel aka XenoAtom.