代码之家  ›  专栏  ›  技术社区  ›  Timeless

C#有符号和无符号整数到大端字节数组,反之亦然,使用具有“最佳”性能的逐位方式

  •  6
  • Timeless  · 技术社区  · 7 年前

    第二次编辑:

    我认为我最初的测试脚本有一个问题 10000000 实际上,times循环处理的是数组的相同内存位置,这使得 unsafe 版本(由Marc提供 here )比…快得多 bitwise 版本我写了另一个测试脚本,我注意到 按位 不安全的 提供几乎相同的性能。

    循环10000000次

    按位:4218484;UNASFERAW:4101719

    0.0284673328426447545529081831(约2%差异)

    代码如下:

    unsafe class UnsafeRaw
    {
        public static short ToInt16BigEndian(byte* buf)
        {
            return (short)ToUInt16BigEndian(buf);
        }
    
        public static int ToInt32BigEndian(byte* buf)
        {
            return (int)ToUInt32BigEndian(buf);
        }
    
        public static long ToInt64BigEndian(byte* buf)
        {
            return (long)ToUInt64BigEndian(buf);
        }
    
        public static ushort ToUInt16BigEndian(byte* buf)
        {
            return (ushort)((*buf++ << 8) | *buf);
        }
    
        public static uint ToUInt32BigEndian(byte* buf)
        {
            return unchecked((uint)((*buf++ << 24) | (*buf++ << 16) | (*buf++ << 8) | *buf));
        }
    
        public static ulong ToUInt64BigEndian(byte* buf)
        {
            unchecked
            {
                var x = (uint)((*buf++ << 24) | (*buf++ << 16) | (*buf++ << 8) | *buf++);
                var y = (uint)((*buf++ << 24) | (*buf++ << 16) | (*buf++ << 8) | *buf);
                return ((ulong)x << 32) | y;
            }
        }
    }
    
    class Bitwise
    {
        public static short ToInt16BigEndian(byte[] buffer, int beginIndex)
        {
            return unchecked((short)(buffer[beginIndex] << 8 | buffer[beginIndex + 1]));
        }
    
        public static int ToInt32BigEndian(byte[] buffer, int beginIndex)
        {
            return unchecked(buffer[beginIndex] << 24 | buffer[beginIndex + 1] << 16 | buffer[beginIndex + 2] << 8 | buffer[beginIndex + 3]);
        }
    
        public static long ToInt64BigEndian(byte[] buffer, int beginIndex)
        {
            return unchecked((long)buffer[beginIndex] << 56 | (long)buffer[beginIndex + 1] << 48 | (long)buffer[beginIndex + 2] << 40 | (long)buffer[beginIndex + 3] << 32 | (long)buffer[beginIndex + 4] << 24 | (long)buffer[beginIndex + 5] << 16 | (long)buffer[beginIndex + 6] << 8 | buffer[beginIndex + 7]);
        }
    
        public static ushort ToUInt16BigEndian(byte[] buffer, int beginIndex)
        {
            return unchecked((ushort)ToInt16BigEndian(buffer, beginIndex));
        }
    
        public static uint ToUInt32BigEndian(byte[] buffer, int beginIndex)
        {
            return unchecked((uint)ToInt32BigEndian(buffer, beginIndex));
        }
    
        public static ulong ToUInt64BigEndian(byte[] buffer, int beginIndex)
        {
            return unchecked((ulong)ToInt64BigEndian(buffer, beginIndex));
        }
    }
    
    class BufferTest
    {
        static long LongRandom(long min, long max, Random rand)
        {
            long result = rand.Next((Int32)(min >> 32), (Int32)(max >> 32));
            result = result << 32;
            result = result | (long)rand.Next((Int32)min, (Int32)max);
            return result;
        }
    
        public static void Main()
        {
            const int times = 10000000;
            const int index = 100;
    
            Random r = new Random();
    
            Stopwatch sw1 = new Stopwatch();
    
            Console.WriteLine($"loop for {times:##,###} times");
    
            Thread.Sleep(1000);
    
            for (int j = 0; j < times; j++)
            {
                short a = (short)r.Next(short.MinValue, short.MaxValue);
                int b = r.Next(int.MinValue, int.MaxValue);
                long c = LongRandom(int.MinValue, int.MaxValue, r);
                ushort d = (ushort)r.Next(ushort.MinValue, ushort.MaxValue);
                uint e = (uint)r.Next(int.MinValue, int.MaxValue);
                ulong f = (ulong)LongRandom(int.MinValue, int.MaxValue, r);
    
                var arr1 = BitConverter.GetBytes(a);
                var arr2 = BitConverter.GetBytes(b);
                var arr3 = BitConverter.GetBytes(c);
                var arr4 = BitConverter.GetBytes(d);
                var arr5 = BitConverter.GetBytes(e);
                var arr6 = BitConverter.GetBytes(f);
    
                Array.Reverse(arr1);
                Array.Reverse(arr2);
                Array.Reverse(arr3);
                Array.Reverse(arr4);
                Array.Reverse(arr5);
                Array.Reverse(arr6);
    
                var largerArr1 = new byte[1024];
                var largerArr2 = new byte[1024];
                var largerArr3 = new byte[1024];
                var largerArr4 = new byte[1024];
                var largerArr5 = new byte[1024];
                var largerArr6 = new byte[1024];
    
                Array.Copy(arr1, 0, largerArr1, index, arr1.Length);
                Array.Copy(arr2, 0, largerArr2, index, arr2.Length);
                Array.Copy(arr3, 0, largerArr3, index, arr3.Length);
                Array.Copy(arr4, 0, largerArr4, index, arr4.Length);
                Array.Copy(arr5, 0, largerArr5, index, arr5.Length);
                Array.Copy(arr6, 0, largerArr6, index, arr6.Length);
    
                sw1.Start();
                var n1 = Bitwise.ToInt16BigEndian(largerArr1, index);
                var n2 = Bitwise.ToInt32BigEndian(largerArr2, index);
                var n3 = Bitwise.ToInt64BigEndian(largerArr3, index);
                var n4 = Bitwise.ToUInt16BigEndian(largerArr4, index);
                var n5 = Bitwise.ToUInt32BigEndian(largerArr5, index);
                var n6 = Bitwise.ToUInt64BigEndian(largerArr6, index);
                sw1.Stop();
    
                //Console.WriteLine(n1 == a);
                //Console.WriteLine(n2 == b);
                //Console.WriteLine(n3 == c);
                //Console.WriteLine(n4 == d);
                //Console.WriteLine(n5 == e);
                //Console.WriteLine(n6 == f);
            }
    
            Stopwatch sw2 = new Stopwatch();
    
            for (int j = 0; j < times; j++)
            {
                short a = (short)r.Next(short.MinValue, short.MaxValue);
                int b = r.Next(int.MinValue, int.MaxValue);
                long c = LongRandom(int.MinValue, int.MaxValue, r);
                ushort d = (ushort)r.Next(ushort.MinValue, ushort.MaxValue);
                uint e = (uint)r.Next(int.MinValue, int.MaxValue);
                ulong f = (ulong)LongRandom(int.MinValue, int.MaxValue, r);
    
                var arr1 = BitConverter.GetBytes(a);
                var arr2 = BitConverter.GetBytes(b);
                var arr3 = BitConverter.GetBytes(c);
                var arr4 = BitConverter.GetBytes(d);
                var arr5 = BitConverter.GetBytes(e);
                var arr6 = BitConverter.GetBytes(f);
    
                Array.Reverse(arr1);
                Array.Reverse(arr2);
                Array.Reverse(arr3);
                Array.Reverse(arr4);
                Array.Reverse(arr5);
                Array.Reverse(arr6);
    
                var largerArr1 = new byte[1024];
                var largerArr2 = new byte[1024];
                var largerArr3 = new byte[1024];
                var largerArr4 = new byte[1024];
                var largerArr5 = new byte[1024];
                var largerArr6 = new byte[1024];
    
                Array.Copy(arr1, 0, largerArr1, index, arr1.Length);
                Array.Copy(arr2, 0, largerArr2, index, arr2.Length);
                Array.Copy(arr3, 0, largerArr3, index, arr3.Length);
                Array.Copy(arr4, 0, largerArr4, index, arr4.Length);
                Array.Copy(arr5, 0, largerArr5, index, arr5.Length);
                Array.Copy(arr6, 0, largerArr6, index, arr6.Length);
    
                sw2.Start();
                unsafe
                {
                    fixed (byte* p1 = &largerArr1[index], p2 = &largerArr2[index], p3 = &largerArr3[index], p4 = &largerArr4[index], p5 = &largerArr5[index], p6 = &largerArr6[index])
                    {
                        var u1 = UnsafeRaw.ToInt16BigEndian(p1);
                        var u2 = UnsafeRaw.ToInt32BigEndian(p2);
                        var u3 = UnsafeRaw.ToInt64BigEndian(p3);
                        var u4 = UnsafeRaw.ToUInt16BigEndian(p4);
                        var u5 = UnsafeRaw.ToUInt32BigEndian(p5);
                        var u6 = UnsafeRaw.ToUInt64BigEndian(p6);
    
                        //Console.WriteLine(u1 == a);
                        //Console.WriteLine(u2 == b);
                        //Console.WriteLine(u3 == c);
                        //Console.WriteLine(u4 == d);
                        //Console.WriteLine(u5 == e);
                        //Console.WriteLine(u6 == f);
                    }
                }
                sw2.Stop();
            }
    
            Console.WriteLine($"Bitwise: {sw1.ElapsedTicks}; UnsafeRaw: {sw2.ElapsedTicks}");
    
            Console.WriteLine((decimal)sw1.ElapsedTicks / sw2.ElapsedTicks - 1);
    
            Console.ReadKey();
        }
    }
    

    第一次编辑:

    我尝试使用 fixed stackalloc 与…比较 按位 . 看起来按位比 我的测试代码中的方法。

    以下是测量结果:

    • 在循环之后 10000000 秒表频率时间 3515622

    unsafe fixed -2239790滴答声

    按位 -672159滴答声

    unsafe stackalloc -1624166滴答声

    我做错什么了吗?我想 不安全的 将比 按位 .

    代码如下:

    class Bitwise
    {
        public static short ToInt16BigEndian(byte[] buf, int i)
        {
            return (short)((buf[i] << 8) | buf[i + 1]);
        }
    
        public static int ToInt32BigEndian(byte[] buf, int i)
        {
            return (buf[i] << 24) | (buf[i + 1] << 16) | (buf[i + 2] << 8) | buf[i + 3];
        }
    
        public static long ToInt64BigEndian(byte[] buf, int i)
        {
            return (buf[i] << 56) | (buf[i + 1] << 48) | (buf[i + 2] << 40) | buf[i + 3] << 32 | (buf[i + 4] << 24) | (buf[i + 5] << 16) | (buf[i + 6] << 8) | buf[i + 7];
        }
    
        public static ushort ToUInt16BigEndian(byte[] buf, int i)
        {
            ushort value = 0;
    
            for (var j = 0; j < 2; j++)
            {
                value = (ushort)unchecked((value << 8) | buf[j + i]);
            }
    
            return value;
        }
    
        public static uint ToUInt32BigEndian(byte[] buf, int i)
        {
            uint value = 0;
    
            for (var j = 0; j < 4; j++)
            {
                value = unchecked((value << 8) | buf[j + i]);
            }
    
            return value;
        }
    
        public static ulong ToUInt64BigEndian(byte[] buf, int i)
        {
            ulong value = 0;
    
            for (var j = 0; j < 8; j++)
            {
                value = unchecked((value << 8) | buf[i + j]);
            }
    
            return value;
        }
    }
    
    class Unsafe
    {
        public static short ToInt16BigEndian(byte[] buf, int i)
        {
            byte[] arr = new byte[2];
    
            arr[0] = buf[i + 1];
            arr[1] = buf[i];
    
            unsafe
            {
                fixed (byte* ptr = arr)
                {
                    return *(short*)ptr;
                }
            }
        }
    
        public static int ToInt32BigEndian(byte[] buf, int i)
        {
            byte[] arr = new byte[4];
    
            arr[0] = buf[i + 3];
            arr[1] = buf[i + 2];
            arr[2] = buf[i + 1];
            arr[3] = buf[i];
    
            unsafe
            {
                fixed (byte* ptr = arr)
                {
                    return *(int*)ptr;
                }
            }
        }
    
        public static long ToInt64BigEndian(byte[] buf, int i)
        {
            byte[] arr = new byte[8];
    
            arr[0] = buf[i + 7];
            arr[1] = buf[i + 6];
            arr[2] = buf[i + 5];
            arr[3] = buf[i + 6];
            arr[4] = buf[i + 3];
            arr[5] = buf[i + 2];
            arr[6] = buf[i + 1];
            arr[7] = buf[i];
    
            unsafe
            {
                fixed (byte* ptr = arr)
                {
                    return *(long*)ptr;
                }
            }
        }
    
        public static ushort ToUInt16BigEndian(byte[] buf, int i)
        {
            byte[] arr = new byte[2];
    
            arr[0] = buf[i + 1];
            arr[1] = buf[i];
    
            unsafe
            {
                fixed (byte* ptr = arr)
                {
                    return *(ushort*)ptr;
                }
            }
        }
    
        public static uint ToUInt32BigEndian(byte[] buf, int i)
        {
            byte[] arr = new byte[4];
    
            arr[0] = buf[i + 3];
            arr[1] = buf[i + 2];
            arr[2] = buf[i + 1];
            arr[3] = buf[i];
    
            unsafe
            {
                fixed (byte* ptr = arr)
                {
                    return *(uint*)ptr;
                }
            }
        }
    
        public static ulong ToUInt64BigEndian(byte[] buf, int i)
        {
            byte[] arr = new byte[8];
    
            arr[0] = buf[i + 7];
            arr[1] = buf[i + 6];
            arr[2] = buf[i + 5];
            arr[3] = buf[i + 6];
            arr[4] = buf[i + 3];
            arr[5] = buf[i + 2];
            arr[6] = buf[i + 1];
            arr[7] = buf[i];
    
            unsafe
            {
                fixed (byte* ptr = arr)
                {
                    return *(ulong*)ptr;
                }
            }
        }
    }
    
    class UnsafeAlloc
    {
        public static short ToInt16BigEndian(byte[] buf, int i)
        {
            unsafe
            {
                const int length = sizeof(short);
                byte* arr = stackalloc byte[length];
                byte* p = arr;
    
                for (int j = length - 1; j >= 0; j--)
                {
                    *p = buf[i + j];
                    p++;
                }
    
                return *(short*)arr;
            }
        }
    
        public static int ToInt32BigEndian(byte[] buf, int i)
        {
            unsafe
            {
                const int length = sizeof(int);
                byte* arr = stackalloc byte[length];
                byte* p = arr;
    
                for (int j = length - 1; j >= 0; j--)
                {
                    *p = buf[i + j];
                    p++;
                }
    
                return *(int*)arr;
            }
        }
    
        public static long ToInt64BigEndian(byte[] buf, int i)
        {
            unsafe
            {
                const int length = sizeof(long);
                byte* arr = stackalloc byte[length];
                byte* p = arr;
    
                for (int j = length - 1; j >= 0; j--)
                {
                    *p = buf[i + j];
                    p++;
                }
    
                return *(long*)arr;
            }
        }
    
        public static ushort ToUInt16BigEndian(byte[] buf, int i)
        {
            unsafe
            {
                const int length = sizeof(ushort);
                byte* arr = stackalloc byte[length];
                byte* p = arr;
    
                for (int j = length - 1; j >= 0; j--)
                {
                    *p = buf[i + j];
                    p++;
                }
    
                return *(ushort*)arr;
            }
        }
    
        public static uint ToUInt32BigEndian(byte[] buf, int i)
        {
            unsafe
            {
                const int length = sizeof(uint);
                byte* arr = stackalloc byte[length];
                byte* p = arr;
    
                for (int j = length - 1; j >= 0; j--)
                {
                    *p = buf[i + j];
                    p++;
                }
    
                return *(uint*)arr;
            }
        }
    
        public static ulong ToUInt64BigEndian(byte[] buf, int i)
        {
            unsafe
            {
                const int length = sizeof(ulong);
                byte* arr = stackalloc byte[length];
                byte* p = arr;
    
                for (int j = length - 1; j >= 0; j--)
                {
                    *p = buf[i + j];
                    p++;
                }
    
                return *(ulong*)arr;
            }
        }
    }
    
    class Program
    {
        static void Main()
        {
            short a = short.MinValue + short.MaxValue / 2;
            int b = int.MinValue + int.MaxValue / 2;
            long c = long.MinValue + long.MaxValue / 2;
            ushort d = ushort.MaxValue / 2;
            uint e = uint.MaxValue / 2;
            ulong f = ulong.MaxValue / 2;
    
            Console.WriteLine(a);
            Console.WriteLine(b);
            Console.WriteLine(c);
            Console.WriteLine(d);
            Console.WriteLine(e);
            Console.WriteLine(f);
            Console.WriteLine();
    
            var arr1 = BitConverter.GetBytes(a);
            var arr2 = BitConverter.GetBytes(b);
            var arr3 = BitConverter.GetBytes(c);
            var arr4 = BitConverter.GetBytes(d);
            var arr5 = BitConverter.GetBytes(e);
            var arr6 = BitConverter.GetBytes(f);
    
            Array.Reverse(arr1);
            Array.Reverse(arr2);
            Array.Reverse(arr3);
            Array.Reverse(arr4);
            Array.Reverse(arr5);
            Array.Reverse(arr6);
    
            Console.WriteLine(Unsafe.ToInt16BigEndian(arr1, 0));
            Console.WriteLine(Unsafe.ToInt32BigEndian(arr2, 0));
            Console.WriteLine(Unsafe.ToInt64BigEndian(arr3, 0));
            Console.WriteLine(Unsafe.ToUInt16BigEndian(arr4, 0));
            Console.WriteLine(Unsafe.ToUInt32BigEndian(arr5, 0));
            Console.WriteLine(Unsafe.ToUInt64BigEndian(arr6, 0));
            Console.WriteLine();
    
            Console.WriteLine(Bitwise.ToInt16BigEndian(arr1, 0));
            Console.WriteLine(Bitwise.ToInt32BigEndian(arr2, 0));
            Console.WriteLine(Bitwise.ToInt64BigEndian(arr3, 0));
            Console.WriteLine(Bitwise.ToUInt16BigEndian(arr4, 0));
            Console.WriteLine(Bitwise.ToUInt32BigEndian(arr5, 0));
            Console.WriteLine(Bitwise.ToUInt64BigEndian(arr6, 0));
            Console.WriteLine();
    
            Console.WriteLine(UnsafeAlloc.ToInt16BigEndian(arr1, 0));
            Console.WriteLine(UnsafeAlloc.ToInt32BigEndian(arr2, 0));
            Console.WriteLine(UnsafeAlloc.ToInt64BigEndian(arr3, 0));
            Console.WriteLine(UnsafeAlloc.ToUInt16BigEndian(arr4, 0));
            Console.WriteLine(UnsafeAlloc.ToUInt32BigEndian(arr5, 0));
            Console.WriteLine(UnsafeAlloc.ToUInt64BigEndian(arr6, 0));
            Console.WriteLine();
    
            Stopwatch sw = new Stopwatch();
            sw.Start();
    
            int times = 10000000;
    
            var t0 = sw.ElapsedTicks;
            for (int i = 0; i < times; i++)
            {
                Unsafe.ToInt16BigEndian(arr1, 0);
                Unsafe.ToInt32BigEndian(arr2, 0);
                Unsafe.ToInt64BigEndian(arr3, 0);
                Unsafe.ToUInt16BigEndian(arr4, 0);
                Unsafe.ToUInt32BigEndian(arr5, 0);
                Unsafe.ToUInt64BigEndian(arr6, 0);
            }
            var t1 = sw.ElapsedTicks;
    
            var t2 = sw.ElapsedTicks;
            for (int i = 0; i < times; i++)
            {
                Bitwise.ToInt16BigEndian(arr1, 0);
                Bitwise.ToInt32BigEndian(arr2, 0);
                Bitwise.ToInt64BigEndian(arr3, 0);
                Bitwise.ToUInt16BigEndian(arr4, 0);
                Bitwise.ToUInt32BigEndian(arr5, 0);
                Bitwise.ToUInt64BigEndian(arr6, 0);
            }
            var t3 = sw.ElapsedTicks;
    
            var t4 = sw.ElapsedTicks;
            for (int i = 0; i < times; i++)
            {
                UnsafeAlloc.ToInt16BigEndian(arr1, 0);
                UnsafeAlloc.ToInt32BigEndian(arr2, 0);
                UnsafeAlloc.ToInt64BigEndian(arr3, 0);
                UnsafeAlloc.ToUInt16BigEndian(arr4, 0);
                UnsafeAlloc.ToUInt32BigEndian(arr5, 0);
                UnsafeAlloc.ToUInt64BigEndian(arr6, 0);
            }
            var t5 = sw.ElapsedTicks;
    
            Console.WriteLine($"{t1 - t0} {t3 - t2} {t5 - t4}");
    
            Console.ReadKey();
        }
    
        public static string ByteArrayToString(byte[] ba)
        {
            return string.Concat(ba.Select(b => Convert.ToString(b, 2).PadLeft(8, '0')));
        }
    }
    

    原件:

    我对按位转换的方式感到困惑 c# 数据类型 short, int, long ushort, uint, ulong 到a byte array ,反之亦然。

    表演 对我来说真的很重要。

    我知道有一个 缓慢的 使用 BitConverter Array.Reverse ,但性能很差。

    我知道基本上还有两种方法 位转换器 ,一个是 按位 ,另一个是 不安全的

    在研究之后 堆栈溢出 例如:

    Efficient way to read big-endian data in C#

    Bitwise endian swap for various types

    In C#, convert ulong[64] to byte[512] faster?

    Fast string to byte[] conversion

    我试了试 按位 方法一,将所有这些小片段组合成一幅完整的图片。

    15555
    43425534
    54354444354
    432
    234234
    34324432234
    
    15555
    43425534
    -1480130482 // wrong
    432
    234234
    34324432234
    

    我对所有这些位转移更为困惑,这是我的测试代码:

    class Program
    {
        static void Main()
        {
            short a = 15555;
            int b = 43425534;
            long c = 54354444354;
            ushort d = 432;
            uint e = 234234;
            ulong f = 34324432234;
    
            Console.WriteLine(a);
            Console.WriteLine(b);
            Console.WriteLine(c);
            Console.WriteLine(d);
            Console.WriteLine(e);
            Console.WriteLine(f);
    
            var arr1 = BitConverter.GetBytes(a);
            var arr2 = BitConverter.GetBytes(b);
            var arr3 = BitConverter.GetBytes(c);
            var arr4 = BitConverter.GetBytes(d);
            var arr5 = BitConverter.GetBytes(e);
            var arr6 = BitConverter.GetBytes(f);
    
            //Console.WriteLine(ByteArrayToString(arr1));
            //Console.WriteLine(ByteArrayToString(arr2));
            //Console.WriteLine(ByteArrayToString(arr3));
            //Console.WriteLine(ByteArrayToString(arr4));
            //Console.WriteLine(ByteArrayToString(arr5));
            //Console.WriteLine(ByteArrayToString(arr6));
    
            Array.Reverse(arr1);
            Array.Reverse(arr2);
            Array.Reverse(arr3);
            Array.Reverse(arr4);
            Array.Reverse(arr5);
            Array.Reverse(arr6);
    
            //Console.WriteLine(ByteArrayToString(arr1));
            //Console.WriteLine(ByteArrayToString(arr2));
            //Console.WriteLine(ByteArrayToString(arr3));
            //Console.WriteLine(ByteArrayToString(arr4));
            //Console.WriteLine(ByteArrayToString(arr5));
            //Console.WriteLine(ByteArrayToString(arr6));
    
            Console.WriteLine(ToInt16BigEndian(arr1, 0));
            Console.WriteLine(ToInt32BigEndian(arr2, 0));
            Console.WriteLine(ToInt64BigEndian(arr3, 0));
            Console.WriteLine(ToUInt16BigEndian(arr4, 0));
            Console.WriteLine(ToUInt32BigEndian(arr5, 0));
            Console.WriteLine(ToUInt64BigEndian(arr6, 0));
    
            Console.ReadKey();
        }
    
        public static string ByteArrayToString(byte[] ba)
        {
            return string.Concat(ba.Select(b => Convert.ToString(b, 2).PadLeft(8, '0')));
        }
    
        public static short ToInt16BigEndian(byte[] buf, int i)
        {
            return (short)((buf[i] << 8) | buf[i + 1]);
        }
    
        public static int ToInt32BigEndian(byte[] buf, int i)
        {
            return (buf[i] << 24) | (buf[i + 1] << 16) | (buf[i + 2] << 8) | buf[i + 3];
        }
    
        public static long ToInt64BigEndian(byte[] buf, int i)
        {
            return (buf[i] << 56) | (buf[i + 1] << 48) | (buf[i + 2] << 40) | (buf[i + 3] << 32) | (buf[i + 4] << 24) | (buf[i + 5] << 16) | (buf[i + 6] << 8) | buf[i + 7];
        }
    
        public static ushort ToUInt16BigEndian(byte[] buf, int i)
        {
            ushort value = 0;
    
            for (var j = 0; j < 2; j++)
            {
                value = (ushort)unchecked((value << 8) | buf[j + i]);
            }
    
            return value;
        }
    
        public static uint ToUInt32BigEndian(byte[] buf, int i)
        {
            uint value = 0;
    
            for (var j = 0; j < 4; j++)
            {
                value = unchecked((value << 8) | buf[j + i]);
            }
    
            return value;
        }
    
        public static ulong ToUInt64BigEndian(byte[] buf, int i)
        {
            ulong value = 0;
    
            for (var j = 0; j < 8; j++)
            {
                value = unchecked((value << 8) | buf[i + j]);
            }
    
            return value;
        }
    }
    

    我在问一个解决方案 最好的 表演 具有 一致的 代码样式和 清洁度 代码。

    2 回复  |  直到 4 年前
        1
  •  7
  •   Marc Gravell    7 年前

    我在问一个解决方案 最好的 表演 具有 一致的 代码样式和 清洁度 代码。

    不;选择任意两个。你不可能三个都有。例如,如果你在寻找 最好的 表演

    未来 Span<T> ( Span<byte> )在这个场景中非常有用-几个IO API获得了对 跨度(<);T> Memory<T> -但是 目前为止 你最好的选择可能是 unsafe 使用 stackalloc byte* (或使用 fixed byte[] )直接写入,在“另一端”情况下使用偏移移位或掩蔽。

        2
  •  0
  •   Timeless    7 年前

    使用我最新的脚本实现并测试,您仍然可以进一步调整它,但是 Bitwise Unsafe 真的很小。

    环境:

    Intel Core i7-7700 CPU @3.6GHz
    8.00 GB
    64-bit
    Windows 10 Pro
    .NET FRAMEWORK 4.5.1
    

    以下是我的结果:

    Loop for 10 x 10,000,000 times
    
    READ VALUE FROM BUFFER ANY CPU
    
    Bitwise: 27270845; UnsafeRaw: 26828068;
    UnsafeRaw is 1.65% faster than Bitwise
    
    READ VALUE FROM BUFFER X64
    
    Bitwise: 27210757; UnsafeRaw: 26847482;
    UnsafeRaw is 1.35% faster than Bitwise
    
    WRITE VALUE TO BUFFER ANY CPU
    
    Bitwise: 26364519; UnsafeRaw: 26258470;
    UnsafeRaw is 0.40% faster than Bitwise
    
    WRITE VALUE TO BUFFER X64
    
    Bitwise: 25728215; UnsafeRaw: 25733755;
    UnsafeRaw is -0.02% faster than Bitwise
    

    如果有人想做自己的测试,请将我的代码粘贴在这里。

    按位,从缓冲区读取值

    public static short ToInt16BigEndian(this byte[] buffer, int beginIndex)
    {
        return unchecked((short)(buffer[beginIndex] << 8 | buffer[beginIndex + 1]));
    }
    
    public static int ToInt32BigEndian(this byte[] buffer, int beginIndex)
    {
        return unchecked(buffer[beginIndex] << 24 | buffer[beginIndex + 1] << 16 | buffer[beginIndex + 2] << 8 | buffer[beginIndex + 3]);
    }
    
    public static long ToInt64BigEndian(this byte[] buffer, int beginIndex)
    {
        return unchecked((long)buffer[beginIndex] << 56 | (long)buffer[beginIndex + 1] << 48 | (long)buffer[beginIndex + 2] << 40 | (long)buffer[beginIndex + 3] << 32 | (long)buffer[beginIndex + 4] << 24 | (long)buffer[beginIndex + 5] << 16 | (long)buffer[beginIndex + 6] << 8 | buffer[beginIndex + 7]);
    }
    
    public static ushort ToUInt16BigEndian(this byte[] buffer, int beginIndex)
    {
        return unchecked((ushort)ToInt16BigEndian(buffer, beginIndex));
    }
    
    public static uint ToUInt32BigEndian(this byte[] buffer, int beginIndex)
    {
        return unchecked((uint)ToInt32BigEndian(buffer, beginIndex));
    }
    
    public static ulong ToUInt64BigEndian(this byte[] buffer, int beginIndex)
    {
        return unchecked((ulong)ToInt64BigEndian(buffer, beginIndex));
    }
    

    按位,将值写入缓冲区

    public static void SetBuffer(this ushort value, byte[] arr, int beginIndex)
    {
        arr[beginIndex] = (byte)(value >> 8);
        arr[beginIndex + 1] = (byte)value;
    }
    
    public static void SetBuffer(this uint value, byte[] arr, int beginIndex)
    {
        arr[beginIndex] = (byte)(value >> 24);
        arr[beginIndex + 1] = (byte)(value >> 16);
        arr[beginIndex + 2] = (byte)(value >> 8);
        arr[beginIndex + 3] = (byte)value;
    }
    
    public static void SetBuffer(this int value, byte[] arr, int beginIndex)
    {
        arr[beginIndex] = (byte)(value >> 24);
        arr[beginIndex + 1] = (byte)(value >> 16);
        arr[beginIndex + 2] = (byte)(value >> 8);
        arr[beginIndex + 3] = (byte)value;
    }
    
    public static void SetBuffer(this short value, byte[] arr, int beginIndex)
    {
        arr[beginIndex] = (byte)(value >> 8);
        arr[beginIndex + 1] = (byte)value;
    }
    
    public static void SetBuffer(this ulong value, byte[] arr, int beginIndex)
    {
        arr[beginIndex] = (byte)(value >> 56);
        arr[beginIndex + 1] = (byte)(value >> 48);
        arr[beginIndex + 2] = (byte)(value >> 40);
        arr[beginIndex + 3] = (byte)(value >> 32);
        arr[beginIndex + 4] = (byte)(value >> 24);
        arr[beginIndex + 5] = (byte)(value >> 16);
        arr[beginIndex + 6] = (byte)(value >> 8);
        arr[beginIndex + 7] = (byte)value;
    }
    
    public static void SetBuffer(this long value, byte[] arr, int beginIndex)
    {
        arr[beginIndex] = (byte)(value >> 56);
        arr[beginIndex + 1] = (byte)(value >> 48);
        arr[beginIndex + 2] = (byte)(value >> 40);
        arr[beginIndex + 3] = (byte)(value >> 32);
        arr[beginIndex + 4] = (byte)(value >> 24);
        arr[beginIndex + 5] = (byte)(value >> 16);
        arr[beginIndex + 6] = (byte)(value >> 8);
        arr[beginIndex + 7] = (byte)value;
    }
    

    public static short ToInt16BigEndian(byte* buf)
    {
        return (short)ToUInt16BigEndian(buf);
    }
    
    public static int ToInt32BigEndian(byte* buf)
    {
        return (int)ToUInt32BigEndian(buf);
    }
    
    public static long ToInt64BigEndian(byte* buf)
    {
        return (long)ToUInt64BigEndian(buf);
    }
    
    public static ushort ToUInt16BigEndian(byte* buf)
    {
        return (ushort)((*buf++ << 8) | *buf);
    }
    
    public static uint ToUInt32BigEndian(byte* buf)
    {
        return unchecked((uint)((*buf++ << 24) | (*buf++ << 16) | (*buf++ << 8) | *buf));
    }
    
    public static ulong ToUInt64BigEndian(byte* buf)
    {
        unchecked
        {
            var x = (uint)((*buf++ << 24) | (*buf++ << 16) | (*buf++ << 8) | *buf++);
            var y = (uint)((*buf++ << 24) | (*buf++ << 16) | (*buf++ << 8) | *buf);
            return ((ulong)x << 32) | y;
        }
    }
    

    不安全,将值写入缓冲区

    public static void SetBuffer(byte* arr, ushort value, int beginIndex)
    {
        *(arr + beginIndex++) = (byte)(value >> 8);
        *(arr + beginIndex) = (byte)value;
    }
    
    public static void SetBuffer(byte* arr, uint value, int beginIndex)
    {
        *(arr + beginIndex++) = (byte)(value >> 24);
        *(arr + beginIndex++) = (byte)(value >> 16);
        *(arr + beginIndex++) = (byte)(value >> 8);
        *(arr + beginIndex) = (byte)value;
    }
    
    public static void SetBuffer(byte* arr, ulong value, int beginIndex)
    {
        *(arr + beginIndex++) = (byte)(value >> 56);
        *(arr + beginIndex++) = (byte)(value >> 48);
        *(arr + beginIndex++) = (byte)(value >> 40);
        *(arr + beginIndex++) = (byte)(value >> 32);
        *(arr + beginIndex++) = (byte)(value >> 24);
        *(arr + beginIndex++) = (byte)(value >> 16);
        *(arr + beginIndex++) = (byte)(value >> 8);
        *(arr + beginIndex) = (byte)value;
    }
    
    public static void SetBuffer(byte* arr, short value, int beginIndex)
    {
        *(arr + beginIndex++) = (byte)(value >> 8);
        *(arr + beginIndex) = (byte)value;
    }
    
    public static void SetBuffer(byte* arr, int value, int beginIndex)
    {
        *(arr + beginIndex++) = (byte)(value >> 24);
        *(arr + beginIndex++) = (byte)(value >> 16);
        *(arr + beginIndex++) = (byte)(value >> 8);
        *(arr + beginIndex) = (byte)value;
    }
    
    public static void SetBuffer(byte* arr, long value, int beginIndex)
    {
        *(arr + beginIndex++) = (byte)(value >> 56);
        *(arr + beginIndex++) = (byte)(value >> 48);
        *(arr + beginIndex++) = (byte)(value >> 40);
        *(arr + beginIndex++) = (byte)(value >> 32);
        *(arr + beginIndex++) = (byte)(value >> 24);
        *(arr + beginIndex++) = (byte)(value >> 16);
        *(arr + beginIndex++) = (byte)(value >> 8);
        *(arr + beginIndex) = (byte)value;
    }