ZLinq Documentation

repository·main·Indexed 26 days ago

https://github.com/cysharp/zlinq

A zero-allocation LINQ implementation for all .NET platforms, including Unity and Godot, featuring 99% compatibility with .NET 10's LINQ. It provides high-performance capabilities such as LINQ-to-Span, LINQ-to-SIMD, and LINQ-to-Tree for hierarchical data like FileSystem, JSON, and Unity GameObjects. Key features include AsValueEnumerable() for allocation-free chaining, a DropInGenerator for automatic LINQ replacement, and SIMD-accelerated operators via AsVectorizable().

Tokens
7.3K
Snippets
21
Records
38
Agent score
90%

What's inside ZLinq

  1. Optimize Select and Zip operations with AsVectorizable

    main
    To achieve high performance with SIMD-enabled operations, use AsVectorizable() before calling Select or Zip. For maximum efficiency, avoid converting back to standard collections immediately; instead, use ToArray() or CopyTo(destination) to process the results directly into a target buffer or array.
  2. Implement Custom Consuming Operators for ValueEnumerable

    main

    When implementing custom consuming operators (like ForEach or ToImmutableArray) for ValueEnumerable<TEnumerator, TSource>, follow these rules:

    1. Use source.Enumerator with a using statement.
    2. Use e.TryGetNext(out var current) instead of MoveNext() and Current.
    3. For .NET 9+, use the allows ref struct modifier.
    4. Use optimization methods like TryGetSpan(out var span) or TryCopyTo(destination, offset) to improve performance.
    5. Warning: Once TryGetNext is called, the state changes. You cannot call TryGetNext after TryGetSpan or TryCopyTo returns true.
    public static class MyExtensions
    {
        public static void Consume<TEnumerator, TSource>(this ValueEnumerable<TEnumerator, TSource> source)
            where TEnumerator : struct, IValueEnumerator<TSource>
    #if NET9_0_OR_GREATER
            , allows ref struct
    #endif
        {
            using var e = source.Enumerator;
            while (e.TryGetNext(out var current)) 
            {
                // Process current
            }
        }
    }
  3. Use SIMD-accelerated LINQ operators

    main

    In .NET 8+, ZLinq provides SIMD-accelerated versions of common operators when working with Span<T> or T[].

    To explicitly trigger SIMD execution regardless of the LINQ chain state, use using ZLinq.Simd; and call .AsVectorizable() on your collection. This enables optimized versions of Aggregate, All, Any, Count, Select, and Zip that accept a Func argument.

  4. Run ZLinq benchmarks via GitHub Actions

    main

    You can trigger benchmarks using the GitHub CLI (gh). Use the --ref flag to specify the branch and the -f flag to pass configuration or filters.

    Examples:

    • Run with Default config: gh workflow run benchmark.yaml --repo Cysharp/ZLinq --ref $branchName
    • Run with a specific filter: gh workflow run benchmark.yaml --repo Cysharp/ZLinq --ref $branchName -f filter=Benchmark.ReadMeBenchmark*
    • Run with TargetFrameworks config: gh workflow run benchmark.yaml --repo Cysharp/ZLinq --ref $branchName -f config=TargetFrameworks
    • Run with SystemLinq config: gh workflow run benchmark.yaml --repo Cysharp/ZLinq --ref $branchName -f config=SystemLinq
    # Run benchmark with `Default` config
    gh workflow run benchmark.yaml --repo Cysharp/ZLinq --ref $branchName
    
    # Run benchmark with `Default` config with benchmark filter
    gh workflow run benchmark.yaml --repo Cysharp/ZLinq --ref $branchName -f filter=Benchmark.ReadMeBenchmark*
    
    # Run benchmark with `TargetFrameworks` config
    gh workflow run benchmark.yaml --repo Cysharp/ZLinq --ref $branchName -f config=TargetFrameworks
    
    # Run benchmark with `SystemLinq` config
    gh workflow run benchmark.yaml --repo Cysharp/ZLinq --ref $branchName -f config=SystemLinq
  5. Install ZLinq.DropInGenerator for automatic LINQ replacement

    main

    You can use ZLinq.DropInGenerator to automatically replace standard LINQ methods with ZLinq implementations using a Source Generator. This avoids the need to manually call .AsValueEnumerable().

    dotnet add package ZLinq.DropInGenerator
  6. Use LINQ to Tree for hierarchical data

    main

    ZLinq extends LINQ to allow querying hierarchical structures (Trees) using axis-based navigation. By implementing the ITraverser<TTraverser, T> interface, a type becomes compatible with operators like Ancestors, Children, Descendants, BeforeSelf, and AfterSelf.

    Pre-built packages are available for:

    • FileSystem: ZLinq.FileSystem (works with FileInfo/DirectoryInfo)
    • JSON: ZLinq.Json (works with System.Text.Json.JsonNode)
    • Unity: GameObject and Transform (see Unity section)
  7. Run ZLinq benchmarks via Visual Studio

    main

    To run benchmarks using Visual Studio:

    1. Open the ZLinq solution.
    2. Set the solution configuration to Release.
    3. Edit Properties/launchSettings.json to configure your environment.
    4. Select the desired launch profile from the dropdown menu.
    5. Start the benchmark by pressing Ctrl+F5 (Start Without Debugging).
  8. Implement Drop-in Replacement in Unity via ZLinq.DropInGenerator

    main

    To use ZLinq.DropInGenerator as a drop-in replacement for standard LINQ in Unity, you must add the ZLinq.DropInGenerator package via NuGetForUnity (requires Unity 2022.3.12f1 or later).

    For each assembly definition (.asmdef), you must add an assembly attribute to enable specific generated types.

    To support Unity's Native Collections (like NativeArray), use the ZLinqDropInExternalExtension attribute.

    // Enable DropIn for Arrays and Lists in an asmdef
    using ZLinq;
    [assembly: ZLinqDropIn("MyApp", DropInGenerateTypes.Array | DropInGenerateTypes.List)]
    
    // Support Native Collections
    [assembly: ZLinqDropInExternalExtension("ZLinq", "Unity.Collections.NativeArray`1", "ZLinq.Linq.FromNativeArray`1")]
  9. Make custom collection types DropIn compatible

    main

    You can make your own collection types compatible with ZLinq's optimized operators by decorating them with the [ZLinqDropInExtension] attribute. This generates a partial extension class that overrides standard LINQ operators with ZLinq versions.

    For maximum performance (zero-allocation), implement IValueEnumerable<TEnumerator, T> instead of just IEnumerable<T>. When both are implemented, IValueEnumerable takes precedence.

    [ZLinqDropInExtension]
    public class AddOnlyIntList : IEnumerable<int>
    {
        List<int> list = new List<int>();
        public void Add(int x) => list.Add(x);
        public IEnumerator<int> GetEnumerator() => list.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator();
    }
  10. Run ZLinq benchmarks via Commandline

    main

    To run benchmarks from the command line, navigate to the sandbox/Benchmark directory and follow these steps:

    1. Build the project in Release mode: dotnet build -c Release
    2. Run the benchmark using the following command structure: dotnet run -c Release --framework net9.0 --no-build --no-launch-profile -- --filter "*"

    Note: If you are running .NET 10 benchmarks, you must add the -f framework=net10.0 input parameter.

    dotnet build -c Release
    dotnet run -c Release --framework net9.0 --no-build --no-launch-profile -- --filter "*"
  11. Install ZLinq in Unity

    main

    Using ZLinq in Unity requires two steps:

    1. Install ZLinq via NuGet using NuGetForUnity. Search for "ZLinq" in the NuGet Package Manager and install it.
    2. Install the ZLinq.Unity package by adding the following Git URL in the Unity Package Manager:

    https://github.com/Cysharp/ZLinq.git?path=src/ZLinq.Unity/Assets/ZLinq.Unity

    https://github.com/Cysharp/ZLinq.git?path=src/ZLinq.Unity/Assets/ZLinq.Unity