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

前端 未结 5 1871
逝去的感伤
逝去的感伤 2020-12-16 21:24

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

相关标签:
5条回答
  • 2020-12-16 22:02

    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);
    }
    
    0 讨论(0)
  • 2020-12-16 22:11

    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

    0 讨论(0)
  • 2020-12-16 22:13

    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.

    0 讨论(0)
  • 2020-12-16 22:17
    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

    0 讨论(0)
  • 2020-12-16 22:22

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题