• Stars
    star
    160
  • Rank 234,663 (Top 5 %)
  • Language
    C#
  • Created about 3 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

HonkPerf.NET

Collection of performant (but limited in usage) replacements for BCL's types and methods;

RefLinq

Summary

RefLinq is like Linq, but it must be only used within a method, so behaves more limited than ref structs. Its benefit - it does not make allocations for enumerators and captured variables (given that you use it properly). Its API does not differ much from that of Linq.

So, basically given that you have some sequence, you do .ToRefLinq() on it, and the rest of API is the same. All API which does not make heap allocations is available.

Example:

var arr = new [] { 1, 2, 3, 4 };

...

var localVar = 5;
var seq =
    arr
    .ToRefLinq()                // magic method
    .Select(c => c + 5)
    .Where(c => c % 2 == 0)
    .Select((c, localVar) => c - 6.0 / localVar, localVar) // capture-less capture
    .Append(3)                  // alloc-less append
    .Append(5)
    .Prepend(3)
    .Concat(arr.ToRefLinq().Select(c => c / 1d))  // concatenation
    ;
return seq.Sum() + seq.Max();

In the example above, no heap allocation happens aside from the one to allocate arr in the first line. Though even that may be optimized if you make stack allocation (and use HonkPerf.NET's FixedReadOnlySpan<T>), but that's on your side.

Similar Libraries

Benchmarks

That's yet another linq library. Why use it? Here's a few benchmarks.

We mark bold the one doing the least allocations and italic the fastest one.

Select & Where

Method Mean Error StdDev Median Code Size Gen 0 Allocated
RefLinqCombined 782.3 ns 9.78 ns 9.15 ns 782.0 ns 1,246 B - -
ClassicLinqCombined 1,337.9 ns 26.57 ns 72.73 ns 1,318.1 ns 2,112 B 0.0801 256 B
NoAlloqCombined 1,370.4 ns 27.32 ns 46.39 ns 1,364.9 ns 2,114 B 0.0267 88 B
LinqFasterCombined 789.3 ns 15.74 ns 34.54 ns 793.1 ns 1,971 B 0.4253 1,336 B
HyperlinqCombined 701.8 ns 13.51 ns 16.60 ns 696.8 ns 1,506 B 0.0277 88 B
ValueLinqCombined 795.3 ns 15.84 ns 37.03 ns 782.2 ns 1,364 B 0.0172 56 B
LinqAFCombined 656.9 ns 13.01 ns 23.13 ns 650.3 ns 1,807 B 0.0277 88 B
StructLinqCombined 752.9 ns 14.93 ns 18.88 ns 751.4 ns 933 B 0.0582 184 B

Append & Prepend

Method Mean Error StdDev Gen 0 Allocated
RefLinqBench 96.51 ns 1.968 ns 3.931 ns - -
LinqBench 248.80 ns 4.945 ns 11.263 ns 0.1938 608 B
HyperLinqBench 293.72 ns 6.392 ns 18.746 ns 0.2165 680 B
LinqAFBench 253.92 ns 5.094 ns 13.509 ns - -

Select, Where, Zip, Sum

Method Mean Error StdDev Median Code Size Gen 0 Allocated
RefLinqSum 1.870 us 0.0344 us 0.0409 us 1.863 us 2,714 B - -
ClassicLinqSum 2.724 us 0.0562 us 0.1640 us 2.679 us 3,436 B 0.1831 584 B
HyperLinqSum 2.446 us 0.0466 us 0.0816 us 2.431 us 2,780 B 0.1755 560 B
ValueLinqSum 1.574 us 0.0311 us 0.0553 us 1.582 us 3,545 B 0.0401 128 B
LinqAFSum 2.268 us 0.0453 us 0.1201 us 2.250 us 4,228 B 0.0458 152 B

Select, Where, Append, Prepend, Concat, Sum, Max

Method Mean Error StdDev Code Size Gen 0 Allocated
RefLinqCombined 4.193 us 0.0835 us 0.2125 us 2,679 B - -
ClassicLinqCombined 8.053 us 0.1582 us 0.2971 us 3,173 B 0.3204 1,032 B
HyperlinqCombined 7.555 us 0.1397 us 0.2410 us 2,491 B 0.2975 944 B
ValueLinqCombined 5.142 us 0.1021 us 0.1967 us 3,903 B 0.2823 888 B
LinqAFCombined 5.938 us 0.1128 us 0.1299 us 5,431 B 0.0381 120 B

Conclusion

There's no "best" linq library. RefLinq is great at avoiding allocations, but sometimes there are faster alternatives (see benchmarks). It provides good API to avoid allocations on captures: you provide the captured function as a last argument, and accept it in the delegate. It also allows to create value delegates identically to how it's done in other libraries. However, it does not offer hundreds of overloads for different use cases to save a few nanoseconds.

Also, the mentioned libraries have different APIs and different coverage of LINQ methods. Some of them don't require the "magic" method, but they may conflict with LINQ then. Some of them lack methods like Append and Prepend, others don't have Zip, etc. RefLinq is not an exception, and it does not have APIs which would require heap allocation.