I need simple way to convert Memory2D
from CommunityToolkit.HighPerformance package into simple 1D collection-like form. List / Array / Anything like that 1D.
Best way i see by myself is MyMemory2D.ToArray().AsSpan().ToArray()
Is it good approach? Any faster / optimal ways?
I need simple way to convert Memory2D
from CommunityToolkit.HighPerformance package into simple 1D collection-like form. List / Array / Anything like that 1D.
Best way i see by myself is MyMemory2D.ToArray().AsSpan().ToArray()
Is it good approach? Any faster / optimal ways?
Share asked Mar 6 at 19:35 nuclear sweetnuclear sweet 1,1311 gold badge11 silver badges32 bronze badges 5 |3 Answers
Reset to default 3Any faster / optimal ways?[1] - Your approach creates a temporary 2D array (via the first call to ToArray()
) then immediately creates the final 1D array from that via the second ToArray()
It would be faster and more efficient to skip construction of the intermediate 2d array by allocating a 1D array of the correct size, and then copying directly onto it:
public static class MemoryExtensions
{
public static T [] ToArray1D<T>(this Memory2D<T> memory) =>
((ReadOnlyMemory2D<T>)memory).ToArray1D();
public static T [] ToArray1D<T>(this ReadOnlyMemory2D<T> memory)
{
var array = new T[memory.Length];
memory.CopyTo(array);
return array;
}
}
[1]
As to whether this is the simplest or best way, that's a matter of opinion.
As correctly pointed out by dbc ToArray
will allocate an extra array which will hurt the prefromance. In addition to the suggested approach you can also leverage the Memory2D<T>.CopyTo
method:
public static class MemoryExtensions
{
public static T [] ToArray1DViaMemory<T>(this Memory2D<T> memory)
{
var array = new T[memory.Length];
memory.CopyTo(array);
return array;
}
// to investigate the API
public static T [] ToArray1DViaSpan<T>(this Memory2D<T> memory)
{
var array = new T[memory.Length];
memory.Span.CopyTo(array);
return array;
}
// methods by dbc...
}
Which seems to have the same ballpark performance on my machine (via the BenchmarkDotNet):
[MemoryDiagnoser(displayGenColumns: true)]
public class Memory2DToArrayBench
{
public readonly static Memory2D<int> Mem = new [,]
{
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 }
};
[Benchmark]
public int[] ToArray1D() => Mem.ToArray1D();
[Benchmark]
public int[] ToArray1DViaSpan() => Mem.ToArray1DViaSpan();
[Benchmark]
public int[] ToArray1DViaMemory() => Mem.ToArray1DViaMemory();
}
Method | Mean | Error | StdDev | Gen0 | Allocated |
---|---|---|---|---|---|
ToArray1D | 67.18 ns | 2.324 ns | 6.631 ns | 0.0101 | 64 B |
ToArray1DViaSpan | 80.87 ns | 3.950 ns | 11.140 ns | 0.0101 | 64 B |
ToArray1DViaMemory | 69.84 ns | 2.760 ns | 7.963 ns | 0.0101 | 64 B |
Note that there are methods like Memory2D<T>.TryGetMemory
and Span2D<T>.TryGetSpan
which:
Tries to get a
Memory<T>
(Span<T>
) instance, if the underlying buffer is contiguous and small enough.
And can be used in some cases.
The best thing to do would be to not do any copying at all. Just get the Span2D<T>
off it, and then create a normal Span<T>
from that.
public static Span<T> ToSpan1D<T>(this Span2D<T> span) =>
span.TryGetSpan(out var span1) ? span1 : throw new Exception("Span too large or non-contiguous");
public static Span<T> ToSpan1D<T>(this Memory2D<T> memory) =>
memory.Span.ToSpan1D();
You can also use an enumerator and foreach
.
public static IEnumerator<T> GetEnumerator<T>(this Memory2D<T> memory) =>
memory.Span.GetEnumerator();
Memory<T>
? – dbc Commented Mar 6 at 19:59Memory2D<T>.TryGetMemory(Memory<T>)
to try to reinterpretMemory2D
as a contiguous 1D buffer. It isn't always possible though; when a Memory2D is sliced sometimes the result isn't a single contiguous buffer. – dbc Commented Mar 7 at 19:46TryGetMemory(Memory<T>)
returns a reference to the actual underlying memory of theMemory2D
so if you modify it you modify the original also. But in my answer the returned memory is a copy. You could mask the difference by returning aReadOnlyMemory<T>
, but if you are looking to mutate the returned memory, the two approaches give different results. – dbc Commented Mar 7 at 21:48