Skip to content

reegeek/StructLinq

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ffde5a5 · Nov 10, 2023
Nov 11, 2022
Feb 23, 2022
Feb 3, 2023
Nov 29, 2022
Nov 10, 2023
Apr 28, 2019
Nov 11, 2022
Feb 8, 2019
Jun 15, 2021
Nov 14, 2022
Nov 14, 2022
Apr 24, 2021
Apr 24, 2021
Apr 24, 2021

Repository files navigation

StructLinq

GitHub release
Nuget Nuget
Build Status continuous
GitHub stars GitHub forks License

Implementation in C# of LINQ concept with struct to reduce drastically memory allocation and improve performance. Introduce IRefStructEnumerable to improve performance when element are fat struct.



Installation

This library is distributed via NuGet. To install StructLinq :

PM> Install-Package StructLinq

Usage

StructLinq use massively generic concept and struct "specialization".

using StructLinq;
 
int[] array = new [] {1, 2, 3, 4, 5};

int result = array
                .ToStructEnumerable()
                .Where(x => (x & 1) == 0, x=>x)
                .Select(x => x *2, x => x)
                .Sum();

x=>x is used to avoid boxing (and allocation) and to help generic type parameters inference. You can also improve performance by using struct for Where predicate and select function.

Performances

All benchmark results are in here. For example following linq sequence:

   list
     .Where(x => (x & 1) == 0)
     .Select(x => x * 2)
     .Sum();

can be replace by:

  list
    .ToStructEnumerable()
    .Where(x => (x & 1) == 0)
    .Select(x => x * 2)
    .Sum();

or if you want zero allocation by:

 list
   .ToStructEnumerable()
   .Where(x => (x & 1) == 0, x=>x)
   .Select(x => x * 2, x=>x)
   .Sum(x=>x);

or if you want zero allocation and better performance by:

  var where = new WherePredicate();
  var select = new SelectFunction();
  list
    .ToStructEnumerable()
    .Where(ref @where, x => x)
    .Select(ref @select, x => x, x => x)
    .Sum(x => x);

Benchmark results are:

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19042
Intel Core i7-8750H CPU 2.20GHz (Coffee Lake), 1 CPU, 12 logical and 6 physical cores
.NET Core SDK=5.0.101
  [Host]     : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT
  DefaultJob : .NET Core 5.0.1 (CoreCLR 5.0.120.57516, CoreFX 5.0.120.57516), X64 RyuJIT

Method Mean Error StdDev Ratio Gen 0 Gen 1 Gen 2 Allocated
LINQ 65.116 μs 0.6153 μs 0.5756 μs 1.00 - - - 152 B
StructLinqWithDelegate 26.146 μs 0.2402 μs 0.2247 μs 0.40 - - - 96 B
StructLinqWithDelegateZeroAlloc 27.854 μs 0.0938 μs 0.0783 μs 0.43 - - - -
StructLinqZeroAlloc 6.872 μs 0.0155 μs 0.0137 μs 0.11 - - - -

StructLinq is significatively faster than default LINQ implementation.

Features

Duck typing with foreach is available with zero allocation for IStructEnumerable.

BCL

Following class have a StructLinq extension method for IStructEnumerable:

Converters

Following converters are available for :

LINQ Extensions

Following extensions are available for :

Other Extensions

  • LongCount
  • UIntCount
  • Order
  • TryFirst

IRefStructEnumerable

    public interface IRefStructEnumerable<out T, out TEnumerator>
        where TEnumerator : struct, IRefStructEnumerator<T>
    {
        TEnumerator GetEnumerator();
    }

    public interface IRefStructEnumerator<T>
    {
        bool MoveNext();

        void Reset();

        ref T Current { get; }
    }

ref Current allows to avoid copy. I should be very useful when T is a fat struct.

Duck typing with foreach with ref is available with zero allocation for IRefStructEnumerable.

BCL

Following class have a StructLinq extension method for IRefStructEnumerable:

Converters

Following converters are available for :

LINQ Extensions

Following extensions are available for :

  • All
  • Any
  • Concat
  • Contains
  • Count
  • Distinct
  • ElementAt
  • ElementAtOrDefault
  • Except
  • First
  • FirstOrDefault
  • Intersect
  • Last
  • Select
  • Skip
  • SkipWhile
  • Sum
  • Take
  • TakeWhile
  • Union
  • Where

Other Extensions

  • LongCount
  • UIntCount
  • TryFirst