最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Convert Memory2D<T> to 1D array - Stack Overflow

programmeradmin3浏览0评论

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
  • By convert, to you mean copy, or reinterpret / cast as a Memory<T>? – dbc Commented Mar 6 at 19:59
  • @dbc any possible way to get same values in 1D form – nuclear sweet Commented Mar 7 at 19:39
  • As noted in the answer by @GuruStron you can use Memory2D<T>.TryGetMemory(Memory<T>) to try to reinterpret Memory2D 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:46
  • @dbc so basically i can call Memory2D<T>.TryGetMemory(Memory<T>) and if it fails use slower method? – nuclear sweet Commented Mar 7 at 21:37
  • Yes, but I believe that TryGetMemory(Memory<T>) returns a reference to the actual underlying memory of the Memory2D 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 a ReadOnlyMemory<T>, but if you are looking to mutate the returned memory, the two approaches give different results. – dbc Commented Mar 7 at 21:48
Add a comment  | 

3 Answers 3

Reset to default 3

Any 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();
发布评论

评论列表(0)

  1. 暂无评论