AndanteSoft.SpanLinq
1.0.1
dotnet add package AndanteSoft.SpanLinq --version 1.0.1
NuGet\Install-Package AndanteSoft.SpanLinq -Version 1.0.1
<PackageReference Include="AndanteSoft.SpanLinq" Version="1.0.1" />
paket add AndanteSoft.SpanLinq --version 1.0.1
#r "nuget: AndanteSoft.SpanLinq, 1.0.1"
// Install AndanteSoft.SpanLinq as a Cake Addin #addin nuget:?package=AndanteSoft.SpanLinq&version=1.0.1 // Install AndanteSoft.SpanLinq as a Cake Tool #tool nuget:?package=AndanteSoft.SpanLinq&version=1.0.1
SpanLinq
Lightweight, Zero Allocation LINQ Implementation on Span<T>
<a href="https://www.nuget.org/packages/AndanteSoft.SpanLinq"></a>
<a href="LICENSE">
</a>
<a href="https://www.nuget.org/packages/AndanteSoft.SpanLinq">
</a>
Usage
It's almost the same as LINQ, but it's implemented as an extension method on Span<T>
/ ReadOnlySpan<T>
and can also be accessed starting from a SpanEnumerable
.
using SpanLinq;
string[] array =
SpanEnumerable.Range(0, 10)
.Skip(2)
.Where(i => i % 2 == 0)
.Select(i => i.ToString())
.ToArray();
string largestString = array.AsSpan()
.Union(new[] { "apple", "banana" })
.MaxBy(s => s.Length);
Install
SpanLinq can be installed from NuGet AndanteSoft.SpanLinq
.
dotnet add package AndanteSoft.SpanLinq
Unity
Supported version: 2021.2 or later. (API Compatibility Level: .NET Standard 2.1)
My test environment is 2022.2.17f1.
Use NuGetForUnity to install.
Fork
Build
dotnet build
Run tests
dotnet test
Run benchmarks
dotnet run -c Release --project SpanLinq.Benchmarks
Feature
Pros.
- It covers ALL LINQ methods implemented up to .NET 9 Preview 6. If you're tied to the old .NET you can still benefit from the new methods.
- It produces no GC garbage (except when allocation is unavoidable, such as
ToList()
), reducing pressure on the garbage collector and improving performance. - This requires .NET Standard 2.1, which means it can be used in Unity.
- Like LINQ, it supports deferred execution, although it is somewhat limited by the nature of a
Span<T>
.
Cons.
- No GC garbage is not magic. It utilizes memory pools, so memory consumption during execution can be relatively high. (TODO: citation needed)
- Some methods are not fully optimized and may result in slower performance than LINQ.
- Of course it depends on the method. Some are twice as fast, some are twice as slow. See Performance.
Performance
System***
areSystem.Linq.Enumerable.***
Span***
areSpanLinq.SpanEnumerable.***
Handcrafted***
are optimized codes that usesSpan<T>
directly.
Specific differences between System.Linq.Enumerable
SpanLinq
is designed to be a nearly complete replacement for System.Linq
, but there are some differences in behavior and implementation.
Cannot be enumerated more than once
Queries cannot be cached, for example:
var query = SpanEnumrable.Range(0, 10).Where(i => i % 2 == 0);
int min = query.Min();
// NG: Cannot use a query twice, results are undefined
int max = query.Max();
To avoid this, materialize query using ToArray()
or CopyTo()
etc.
AsEnumerable()
AsEnumerable()
is a method that is almost meaningless in System.Linq
, but since Span<T>
is not IEnumerable<T>
, this method converts Span<T>
into a class that implements IEnumerable<T>
and returns it.
Currently, it allocates 32 B per call.
Cast()
Casting from a struct to a interface incurs an allocation by boxing, which cannot be avoided in principle.
Chunk()
Since it returns an array, allocation occurs.
CopyTo()
This method copies the current sequence to an existing span, similar to Span<T>.CopyTo()
.
ToArray()
inherently causes allocation, but this method does not.
ForEach()
This method does not exist in LINQ. This is the original implementation.
This method calls Action<T>
on every element of a sequence. This method executes immediately.
SpanEnumerable.FromEnumerable()
This method converts an IEnumerable<T>
into a SpanEnumerator<>
.
This allows sequences implemented with yield return
to be handled by a SpanEnumerator<>
.
static IEnumerable<string> Sample()
{
yield return "Alice";
yield return "Barbara";
yield return "Charlotte";
}
var spanEnumerable = SpanEnumerable.FromEnumerable(Sample());
GroupBy()
Since it returns IGrouping<TKey, TElement>
, allocation occurs.
Shuffle()
This method does not exist in LINQ. This is the original implementation.
Shuffle()
randomly shuffles the order of the elements of a sequence.
If you need reproducibility, call it as Shuffle(random)
.
Sum()
It returns int
/double
/etc. instead of int?
/double?
.
ToArrayPool()
This method gets a shared array via ArrayPool<T>.Shared.Rent()
and returns the array and a span.
When you're done with the data, you must return the array via ArrayPool<T>.Shared.Return()
.
ToArray()
/ ToDictionary()
/ ToHashSet()
/ ToList()
/ ToLookup()
It is by nature that allocation occurs.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- System.Runtime.CompilerServices.Unsafe (>= 6.0.0)
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.0.1 | 175 | 8/6/2024 |
1.0.0 | 80 | 8/3/2024 |
0.1.0-alpha2 | 67 | 8/3/2024 |