Copy array to struct array as fast as possible in C#

我的未来我决定 提交于 2019-12-18 04:16:25

问题


I am working with Unity 4.5, grabbing images as bytes arrays (each byte represent a channel, taking 4 bytes per pixel (rgba) and displaying them on a texture converting the array to a Color32 array, using this loop:

   img = new Color32[byteArray.Length / nChannels]; //nChannels being 4
   for (int i=0; i< img.Length; i++) {
        img[i].r = byteArray[i*nChannels];
        img[i].g = byteArray[i*nChannels+1];
        img[i].b = byteArray[i*nChannels+2];
        img[i].a = byteArray[i*nChannels+3];
    }

Then, it is applied to the texture using:

tex.SetPixels32(img);

However, this slows down the application significantly (this loop is executed on every single frame), and I would like to know if there is any other way to speed up the copying process. I've found some people (Fast copy of Color32[] array to byte[] array) using the Marshal.Copy functions in order to do the reverse process (Color32 to byte array), but I have not been able to make it work to copy a byte array to a Color32 array. Does anybody know a faster way?

Thank you in advance!


回答1:


Yes, Marshal.Copy is the way to go. I've answered a similar question here.

Here's a generic method to copy from struct[] to byte[] and vice versa

private static byte[] ToByteArray<T>(T[] source) where T : struct
{
    GCHandle handle = GCHandle.Alloc(source, GCHandleType.Pinned);
    try
    {
        IntPtr pointer = handle.AddrOfPinnedObject();
        byte[] destination = new byte[source.Length * Marshal.SizeOf(typeof(T))];
        Marshal.Copy(pointer, destination, 0, destination.Length);
        return destination;
    }
    finally
    {
        if (handle.IsAllocated)
            handle.Free();
    }
}

private static T[] FromByteArray<T>(byte[] source) where T : struct
{
    T[] destination = new T[source.Length / Marshal.SizeOf(typeof(T))];
    GCHandle handle = GCHandle.Alloc(destination, GCHandleType.Pinned);
    try
    {
        IntPtr pointer = handle.AddrOfPinnedObject();
        Marshal.Copy(source, 0, pointer, source.Length);
        return destination;
    }
    finally
    {
        if (handle.IsAllocated)
            handle.Free();
    }
}

Use it as:

[StructLayout(LayoutKind.Sequential)]
public struct Demo
{
    public double X;
    public double Y;
}

private static void Main()
{
    Demo[] array = new Demo[2];
    array[0] = new Demo { X = 5.6, Y = 6.6 };
    array[1] = new Demo { X = 7.6, Y = 8.6 };

    byte[] bytes = ToByteArray(array);
    Demo[] array2 = FromByteArray<Demo>(bytes);
}



回答2:


This code requires unsafe switch but should be fast. I think you should benchmark these answers...

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

var colors = MemCopyUtils.ByteArrayToColor32Array(bytes);

public class MemCopyUtils
{
    unsafe delegate void MemCpyDelegate(byte* dst, byte* src, int len);
    static MemCpyDelegate MemCpy;

    static MemCopyUtils()
    {
        InitMemCpy();
    }

    static void InitMemCpy()
    {
        var mi = typeof(Buffer).GetMethod(
            name: "Memcpy",
            bindingAttr: BindingFlags.NonPublic | BindingFlags.Static,
            binder:  null,
            types: new Type[] { typeof(byte*), typeof(byte*), typeof(int) },
            modifiers: null);
        MemCpy = (MemCpyDelegate)Delegate.CreateDelegate(typeof(MemCpyDelegate), mi);
    }

    public unsafe static Color32[] ByteArrayToColor32Array(byte[] bytes)
    {
        Color32[] colors = new Color32[bytes.Length / sizeof(Color32)];

        fixed (void* tempC = &colors[0])
        fixed (byte* pBytes = bytes)
        {
            byte* pColors = (byte*)tempC;
            MemCpy(pColors, pBytes, bytes.Length);
        }
        return colors;
    }
}



回答3:


Using Parallel.For may give you a significant performance increase.

img = new Color32[byteArray.Length / nChannels]; //nChannels being 4
Parallel.For(0, img.Length, i =>
{
    img[i].r = byteArray[i*nChannels];
    img[i].g = byteArray[i*nChannels+1];
    img[i].b = byteArray[i*nChannels+2];
    img[i].a = byteArray[i*nChannels+3];
});

Example on MSDN




回答4:


I haven't profiled it, but using fixed to ensure your memory doesn't get moved around and to remove bounds checks on array accesses might provide some benefit:

img = new Color32[byteArray.Length / nChannels]; //nChannels being 4
fixed (byte* ba = byteArray)
{
    fixed (Color32* c = img)
    {
        byte* byteArrayPtr = ba;
        Color32* colorPtr = c;
        for (int i = 0; i < img.Length; i++)
        {
            (*colorPtr).r = *byteArrayPtr++;
            (*colorPtr).g = *byteArrayPtr++;
            (*colorPtr).b = *byteArrayPtr++;
            (*colorPtr).a = *byteArrayPtr++;
            colorPtr++;
        }
    }
}

It might not provide much more benefit on 64-bit systems - I believe that the bounds checking is is more highly optimized. Also, this is an unsafe operation, so take care.




回答5:


public Color32[] GetColorArray(byte[] myByte)
{
    if (myByte.Length % 1 != 0) 
       throw new Exception("Must have an even length");

    var colors = new Color32[myByte.Length / nChannels];

    for (var i = 0; i < myByte.Length; i += nChannels)
    {
       colors[i / nChannels] = new Color32(
           (byte)(myByte[i] & 0xF8),
           (byte)(((myByte[i] & 7) << 5) | ((myByte[i + 1] & 0xE0) >> 3)),
           (byte)((myByte[i + 1] & 0x1F) << 3),
           (byte)1);
    }

    return colors;
}

Worked about 30-50 times faster than just i++. The "extras" is just styling. This code is doing, in one "line", in the for loop, what you're declaring in 4 lines plus it is much quicker. Cheers :)

Referenced + Referenced code: Here



来源:https://stackoverflow.com/questions/25311361/copy-array-to-struct-array-as-fast-as-possible-in-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!