溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C#處理類型和二進(jìn)制數(shù)據(jù)轉(zhuǎn)換并提高程序性能的方法

發(fā)布時(shí)間:2022-04-02 11:04:24 來源:億速云 閱讀:329 作者:iii 欄目:開發(fā)技術(shù)

這篇“C#處理類型和二進(jìn)制數(shù)據(jù)轉(zhuǎn)換并提高程序性能的方法”文章的知識點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“C#處理類型和二進(jìn)制數(shù)據(jù)轉(zhuǎn)換并提高程序性能的方法”文章吧。

    C# 原語類型

    按照內(nèi)存分配來區(qū)分,C# 有值類型、引用類型;

    按照基礎(chǔ)類型類型來分,C# 有 內(nèi)置類型、通用類型、自定義類型、匿名類型、元組類型、CTS類型(通用類型系統(tǒng));

    C# 的基礎(chǔ)類型包括:

    • 整型: sbyte, byte, short, ushort, int, uint, long, ulong

    • 實(shí)數(shù)類型: float, double, decimal

    • 字符類型: char

    • 布爾類型: bool

    • 字符串類型: string

    C# 中的原語類型,是基礎(chǔ)類型中的值類型,不包括 string。原語類型可以使用 sizeof() 來獲取字節(jié)大小,除 bool 外,都有 MaxValue、MinValue 兩個(gè)字段。

    sizeof(uint);
    uint.MaxValue
    uint.MinValue

    我們也可以在泛型上進(jìn)行區(qū)分,上面的教程類型,除了 string,其他類型都是 struct。

    <T>() where T : struct
    {
    }

    1,利用 Buffer 優(yōu)化數(shù)組性能

    Buffer 可以操作基元類型(int、byte等)的數(shù)組,利用.NET 中的 Buffer 類,通過更快地訪問內(nèi)存中的數(shù)據(jù)來提高應(yīng)用程序的性能。
    Buffer 可以直接從基元類型的數(shù)組中,直接取出指定數(shù)量的字節(jié),或者給其某個(gè)字節(jié)設(shè)置值。

    Buffer 主要在直接操作內(nèi)存數(shù)據(jù)、操作非托管內(nèi)存時(shí),使用 Buffer 可以帶來安全且高性能的體驗(yàn)。

    方法說明
    BlockCopy(Array, Int32, Array, Int32, Int32)將指定數(shù)目的字節(jié)從起始于特定偏移量的源數(shù)組復(fù)制到起始于特定偏移量的目標(biāo)數(shù)組。
    ByteLength(Array)返回指定數(shù)組中的字節(jié)數(shù)。
    GetByte(Array, Int32)檢索指定數(shù)組中指定位置的字節(jié)。
    MemoryCopy(Void, Void, Int64, Int64)將指定為長整型值的一些字節(jié)從內(nèi)存中的一個(gè)地址復(fù)制到另一個(gè)地址。此 API 不符合 CLS。
    MemoryCopy(Void, Void, UInt64, UInt64)將指定為無符號長整型值的一些字節(jié)從內(nèi)存中的一個(gè)地址復(fù)制到另一個(gè)地址。此 API 不符合 CLS。
    SetByte(Array, Int32, Byte)將指定的值分配給指定數(shù)組中特定位置處的字節(jié)。

    下面來介紹一下 Buffer 的一些使用方法。

    BlockCopy 可以復(fù)制數(shù)組的一部分到另一個(gè)數(shù)組,其使用方法如下:

            int[] arr1 = new int[] { 1, 2, 3, 4, 5 };
            int[] arr2 = new int[10] { 0, 0, 0, 0, 0, 6, 7, 8, 9, 10 };
    
            // int = 4 byte
            // index:       0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 ... ...
            // arr1:        01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00
            // arr2:        00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00 09 00 00 00 0A 00 00 00
    
            // Buffer.ByteLength(arr1) == 20 ,
            // Buffer.ByteLength(arr2) == 40
    
    
            Buffer.BlockCopy(arr1, 0, arr2, 0, 19);
    
            for (int i = 0; i < arr2.Length; i++)
            {
                Console.Write(arr2[i] + ",");
            }

    .SetByte() 則可細(xì)粒度地設(shè)置數(shù)組的值,即可以直接設(shè)置數(shù)組中任意一位的值,其使用方法如下:

            //source data:
            // 0000,0001,0002,00003,0004
            // 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
            int[] a = new int[] { 0, 1, 2, 3, 4 };
            foreach (var item in a)
            {
                Console.Write(item + ",");
            }
    
            Console.WriteLine("\n------\n");
    
            // see : https://stackoverflow.com/questions/26455843/how-are-array-values-stored-in-little-endian-vs-big-endian-architecture
            // memory save that data:
            // 0000    1000    2000    3000    4000
            for (int i = 0; i < Buffer.ByteLength(a); i++)
            {
                Console.Write(Buffer.GetByte(a, i));
                if (i != 0 && (i + 1) % 4 == 0)
                    Console.Write("    ");
            }
    
            // 16 進(jìn)制
            // 0000    1000    2000    3000    4000
    
            Console.WriteLine("\n------\n");
    
            Buffer.SetByte(a, 0, 4);
            Buffer.SetByte(a, 4, 3);
            Buffer.SetByte(a, 8, 2);
            Buffer.SetByte(a, 12, 1);
            Buffer.SetByte(a, 16, 0);
    
            foreach (var item in a)
            {
                Console.Write(item + ",");
            }
    
            Console.WriteLine("\n------\n");

    建議自行測試,斷點(diǎn)調(diào)試,觀察過程。

    2,BinaryPrimitives 細(xì)粒度操作字節(jié)數(shù)組

    System.Buffers.Binary.BinaryPrimitives 用來以精確的方式讀取或者字節(jié)數(shù)組,只能對 byte 或 byte 數(shù)組使用,其使用場景非常廣泛。

    BinaryPrimitives 的實(shí)現(xiàn)原理是 BitConverter,BinaryPrimitives 對 BitConverter 做了一些封裝。BinaryPrimitives 的主要使用方式是以某種形式從 byte 或 byte 數(shù)組中讀取出信息。

    例如,BinaryPrimitives 在 byte 數(shù)組中,一次性讀取四個(gè)字節(jié),其示例代碼如下:

            // source data:  00 01 02 03 04
            // binary data:  00000000 00000001 00000010 00000011 000001000
            byte[] arr = new byte[] { 0, 1, 2, 3, 4, };
    
            // read one int,4 byte
            int head = BinaryPrimitives.ReadInt32BigEndian(arr);
    
    
            // 5 byte:             00000000 00000001 00000010 00000011 000001000
            // read 4 byte(int) :  00000000 00000001 00000010 00000011
            //                     = 66051
    
            Console.WriteLine(head);

    在 BinaryPrimitives 中有大端小端之分。在 C# 中,應(yīng)該都是小端在前大端在后的,具體可能會因處理器架構(gòu)而不同。
    你可以使用 BitConverter.IsLittleEndian 來判斷在當(dāng)前處理器上,C# 程序是大端還是小端在前。

    以 .Read...() 開頭的方法,可以以字節(jié)為定位訪問 byte 數(shù)組上的數(shù)據(jù)。

    以 .Write...() 開頭的方法,可以向某個(gè)位置寫入數(shù)據(jù)。

    下面舉個(gè)例子:

            // source data:  00 01 02 03 04
            // binary data:  00000000 00000001 00000010 00000011 000001000
            byte[] arr = new byte[] { 0, 1, 2, 3, 4, };
    
            // read one int,4 byte
            // 5 byte:             00000000 00000001 00000010 00000011 000001000
            // read 4 byte(int) :  00000000 00000001 00000010 00000011
            //                     = 66051
    
            int head = BinaryPrimitives.ReadInt32BigEndian(arr);
            Console.WriteLine(head);
    
            // BinaryPrimitives.WriteInt32LittleEndian(arr, 1);
            BinaryPrimitives.WriteInt32BigEndian(arr.AsSpan().Slice(0, 4), 0b00000000_00000000_00000000_00000001);
            // to : 00000000 00000000 00000000 00000001 |  000001000
            // read 4 byte
    
            head = BinaryPrimitives.ReadInt32BigEndian(arr);
            Console.WriteLine(head);

    建議自行測試,斷點(diǎn)調(diào)試,觀察過程。

    提高代碼安全性

    C#和.NET Core 有的許多面向性能的 API,C# 和 .NET 的一大優(yōu)點(diǎn)是可以在不犧牲內(nèi)存安全性的情況下編寫快速出高性能的庫。我們在避免使用 unsafe 代碼的情況下,通過二進(jìn)制處理類,我們可以編寫出高性能的代碼和具有安全性的代碼。

    在 C# 中,我們有以下類型可以高效操作字節(jié)/內(nèi)存:

    • Span 和C#類型可以快速安全地訪問內(nèi)存。表示任意內(nèi)存的連續(xù)區(qū)域。使用 span 使我們可以序列化為托管.NET數(shù)組,堆棧分配的數(shù)組或非托管內(nèi)存,而無需使用指針。.NET可以防止緩沖區(qū)溢出。

    • ref struct 、 Span

    • stackalloc 用于創(chuàng)建基于堆棧的數(shù)組。stackalloc 是在需要較小緩沖區(qū)時(shí)避免分配的有用工具。

    • 低級方法,并在原始類型和字節(jié)之間直接轉(zhuǎn)換。MemoryMarshal.GetReference() 、Unsafe.ReadUnaligned() 、Unsafe.WriteUnaligned()

    • BinaryPrimitives具有用于在.NET基本類型和字節(jié)之間進(jìn)行有效轉(zhuǎn)換的輔助方法。例如,讀取小尾數(shù)字節(jié)并返回?zé)o符號的64位數(shù)字。所提供的方法經(jīng)過了最優(yōu)化,并使用了向量化。BinaryPrimitives.ReadUInt64LittleEndian、BinaryPrimitive

    以 .Reverse...() 開頭的方法,可以置換基元類型的大小端。

            short value = 0b00000000_00000001;
            // to endianness: 0b00000001_00000000 == 256
            BinaryPrimitives.ReverseEndianness(0b00000000_00000000_00000000_00000001);
    
            Console.WriteLine(BinaryPrimitives.ReverseEndianness(value));
    
            value = 0b00000001_00000000;
            Console.WriteLine(BinaryPrimitives.ReverseEndianness(value));
            // 1

    3,BitConverter、MemoryMarshal

    BitConverter 可以基元類型和 byte 相互轉(zhuǎn)換,例如 int 和 byte 互轉(zhuǎn),或者任意取出、寫入基元類型的任意一個(gè)字節(jié)。
    其示例如下:

            // 0b...1_00000100
            int value = 260;
    		
            // byte max value:255
            // a = 0b00000100; 丟失 int ... 00000100 之前的位數(shù)。
            byte a = (byte)value;
    
            // a = 4
            Console.WriteLine(a);
    
            // LittleEndian
            // 0b 00000100 00000001 00000000 00000000
            byte[] b = BitConverter.GetBytes(260);
            Console.WriteLine(Buffer.GetByte(b, 1)); // 4
    
            if (BitConverter.IsLittleEndian)
                Console.WriteLine(BinaryPrimitives.ReadInt32LittleEndian(b));
            else
                Console.WriteLine(BinaryPrimitives.ReadInt32BigEndian(b));

    MemoryMarshal 提供與 Memory<T>、ReadOnlyMemory<T>、Span<T> 和 ReadOnlySpan<T> 進(jìn)行交互操作的方法。

    MemoryMarshal 在 System.Runtime.InteropServices 命名空間中。

    我們先介紹 MemoryMarshal.Cast(),它可以將一種基元類型的范圍強(qiáng)制轉(zhuǎn)換為另一種基元類型的范圍。

            // 1 int  = 4 byte
            // int [] {1,2}
            // 0001     0002
            var byteArray = new byte[] { 1, 0, 0, 0, 2, 0, 0, 0 };
            Span<byte> byteSpan = byteArray.AsSpan();
            // byte to int 
            Span<int> intSpan = MemoryMarshal.Cast<byte, int>(byteSpan);
            foreach (var item in intSpan)
            {
                Console.Write(item + ",");
            }

    最簡單的說法是,MemoryMarshal 可以將一種結(jié)構(gòu)轉(zhuǎn)換為另一種結(jié)構(gòu)。

    我們可以將一個(gè)結(jié)構(gòu)轉(zhuǎn)換為字節(jié):

    public struct Test
    {
        public int A;
        public int B;
        public int C;
    }
    
    ... ...
    
            Test test = new Test()
            {
                A = 1,
                B = 2,
                C = 3
            };
            var testArray = new Test[] { test };
            ReadOnlySpan<byte> tmp = MemoryMarshal.AsBytes(testArray.AsSpan());
    
            // socket.Send(tmp); ...

    還可以逆向還原字節(jié)為結(jié)構(gòu)體:

            // bytes = socket.Accept(); .. 
            ReadOnlySpan<Test> testSpan = MemoryMarshal.Cast<byte,Test>(tmp);
    
            // or
            Test testSpan = MemoryMarshal.Read<Test>(tmp);

    例如,我們要對比兩個(gè)結(jié)構(gòu)體數(shù)組中,每個(gè)結(jié)構(gòu)體是否相等,可以采用以下代碼:

            static void Main(string[] args)
            {
                int[] a = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
                int[] b = new int[] { 1, 2, 3, 4, 5, 6, 7, 0, 9 };
                _ = Compare64(a,b);
            }
    
            private static bool Compare64<T>(T[] t1, T[] t2)
                where T : struct
            {
                var l1 = MemoryMarshal.Cast<T, long>(t1);
                var l2 = MemoryMarshal.Cast<T, long>(t2);
    
                for (int i = 0; i < l1.Length; i++)
                {
                    if (l1[i] != l2[i]) return false;
                }
                return true;
            }

    后面有個(gè)更好的性能提升方案。

    程序員基本都學(xué)習(xí)過 C 語言,應(yīng)該了解 C 語言中的結(jié)構(gòu)體字節(jié)對齊,在 C# 中也是一樣,兩種類型相互轉(zhuǎn)換,除了 C# 結(jié)構(gòu)體轉(zhuǎn) C# 結(jié)構(gòu)體,也可以 C 語言結(jié)構(gòu)體轉(zhuǎn) C# 結(jié)構(gòu)體,但是要考慮好字節(jié)對齊,如果兩個(gè)結(jié)構(gòu)體所占用的內(nèi)存大小不一樣,則可能在轉(zhuǎn)換時(shí)出現(xiàn)數(shù)據(jù)丟失或出現(xiàn)錯(cuò)誤。

    4,Marshal

    Marshal 提供了用于分配非托管內(nèi)存,復(fù)制非托管內(nèi)存塊以及將托管類型轉(zhuǎn)換為非托管類型的方法的集合,以及與非托管代碼進(jìn)行交互時(shí)使用的其他方法,或者用來確定對象的大小。

    例如,來確定 C# 中的一些類型大?。?/p>

                Console.WriteLine("SystemDefaultCharSize={0}, SystemMaxDBCSCharSize={1}",
             Marshal.SystemDefaultCharSize, Marshal.SystemMaxDBCSCharSize);

    輸出 char 占用的字節(jié)數(shù)。

    例如,在調(diào)用非托管代碼時(shí),需要傳遞函數(shù)指針,C# 一般使用委托傳遞,很多時(shí)候?yàn)榱吮苊飧鞣N內(nèi)存問題異常問題,需要轉(zhuǎn)換為指針傳遞。

    IntPtr p = Marshal.GetFunctionPointerForDelegate(_overrideCompileMethod)

    Marshal 也可以很方便地獲得一個(gè)結(jié)構(gòu)體的字節(jié)大?。?/p>

    public struct Point
    {
        public Int32 x, y;
    }
    
    Marshal.SizeOf(typeof(Point));

    從非托管內(nèi)存中分配一塊內(nèi)存和釋放內(nèi)存,我們可以避免 usafe 代碼的使用,代碼示例:

            IntPtr hglobal = Marshal.AllocHGlobal(100);
            Marshal.FreeHGlobal(hglobal);

    實(shí)踐

    合理利用前面提到的二進(jìn)制處理類,可以在很多方面提升代碼性能,在前面的學(xué)習(xí)中,我們大概了解這些對象,但是有什么應(yīng)用場景?真的能夠提升性能?有沒有練習(xí)代碼?

    這里筆者舉個(gè)例子,如何比較兩個(gè) byte[] 數(shù)組是否相等?
    最簡單的代碼示例如下:

            public bool ForBytes(byte[] a,byte[] b)
            {
                if (a.Length != b.Length)
                    return false;
    				
                for (int i = 0; i < a.Length; i++)
                {
                    if (a[i] != b[i]) return false;
                }
                return true;
            }

    這個(gè)代碼很簡單,循環(huán)遍歷字節(jié)數(shù)組,一個(gè)個(gè)判斷是否相等。

    如果用上前面的二進(jìn)制處理對象類,則可以這樣寫代碼:

            private static bool EqualsBytes(byte[] b1, byte[] b2)
            {
                var a = b1.AsSpan();
                var b = b2.AsSpan();
                Span<byte> copy1 = default;
                Span<byte> copy2 = default;
    
                if (a.Length != b.Length)
                    return false;
    
                for (int i = 0; i < a.Length;)
                {
                    if (a.Length - 8 > i)
                    {
                        copy1 = a.Slice(i, 8);
                        copy2 = b.Slice(i, 8);
                        if (BinaryPrimitives.ReadUInt64BigEndian(copy1) != BinaryPrimitives.ReadUInt64BigEndian(copy2))
                            return false;
                        i += 8;
                        continue;
                    }
    
                    if (a[i] != b[i])
                        return false;
                    i++;
                }
                return true;
            }

    你可能會在想,第二種方法,這么多代碼,這么多判斷,還有各種函數(shù)調(diào)用,還多創(chuàng)建了一些對象,這特么能夠提升速度?這樣會不會消耗更多內(nèi)存??? 別急,你可以使用以下完整代碼測試: 

    using BenchmarkDotNet.Attributes;
    using BenchmarkDotNet.Jobs;
    using BenchmarkDotNet.Running;
    using System;
    using System.Buffers.Binary;
    using System.Runtime.InteropServices;
    using System.Text;
    
    namespace BenTest
    {
        [SimpleJob(RuntimeMoniker.NetCoreApp31)]
        [SimpleJob(RuntimeMoniker.CoreRt31)]
        [RPlotExporter]
        public class Test
        {
            private byte[] _a = Encoding.UTF8.GetBytes("5456456456444444444444156456454564444444444444444444444444444444444444444777777777777777777777711111111111116666666666666");
            private byte[] _b = Encoding.UTF8.GetBytes("5456456456444444444444156456454564444444444444444444444444444444444444444777777777777777777777711111111111116666666666666");
    
            private int[] A1 = new int[] { 41544444, 4487, 841, 8787, 4415, 7, 458, 4897, 87897, 815, 485, 4848, 787, 41, 5489, 74878, 84, 89787, 8456, 4857489, 784, 85489, 47 };
            private int[] B2 = new int[] { 41544444, 4487, 841, 8787, 4415, 7, 458, 4897, 87897, 815, 485, 4848, 787, 41, 5489, 74878, 84, 89787, 8456, 4857489, 784, 85489, 47 };
    
            [Benchmark]
            public bool ForBytes()
            {
                for (int i = 0; i < _a.Length; i++)
                {
                    if (_a[i] != _b[i]) return false;
                }
                return true;
            }
    
            [Benchmark]
            public bool ForArray()
            {
                return ForArray(A1, B2);
            }
    
            private bool ForArray<T>(T[] b1, T[] b2) where T : struct
            {
                for (int i = 0; i < b1.Length; i++)
                {
                    if (!b1[i].Equals(b2[i])) return false;
                }
                return true;
            }
    
            [Benchmark]
            public bool EqualsArray()
            {
                return EqualArray(A1, B2);
            }
    
            [Benchmark]
            public bool EqualsBytes()
            {
                var a = _a.AsSpan();
                var b = _b.AsSpan();
                Span<byte> copy1 = default;
                Span<byte> copy2 = default;
    
                if (a.Length != b.Length)
                    return false;
    
                for (int i = 0; i < a.Length;)
                {
                    if (a.Length - 8 > i)
                    {
                        copy1 = a.Slice(i, 8);
                        copy2 = b.Slice(i, 8);
                        if (BinaryPrimitives.ReadUInt64BigEndian(copy1) != BinaryPrimitives.ReadUInt64BigEndian(copy2))
                            return false;
                        i += 8;
                        continue;
                    }
    
                    if (a[i] != b[i])
                        return false;
                    i++;
                }
                return true;
            }
    
            private bool EqualArray<T>(T[] t1, T[] t2) where T : struct
            {
                Span<byte> b1 = MemoryMarshal.AsBytes<T>(t1.AsSpan());
                Span<byte> b2 = MemoryMarshal.AsBytes<T>(t2.AsSpan());
    
                Span<byte> copy1 = default;
                Span<byte> copy2 = default;
    
                if (b1.Length != b2.Length)
                    return false;
    
                for (int i = 0; i < b1.Length;)
                {
                    if (b1.Length - 8 > i)
                    {
                        copy1 = b1.Slice(i, 8);
                        copy2 = b2.Slice(i, 8);
                        if (BinaryPrimitives.ReadUInt64BigEndian(copy1) != BinaryPrimitives.ReadUInt64BigEndian(copy2))
                            return false;
                        i += 8;
                        continue;
                    }
    
                    if (b1[i] != b2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                var summary = BenchmarkRunner.Run<Test>();
                Console.ReadKey();
            }
        }
    }

    使用 BenchmarkDotNet 的測試結(jié)果如下:

    BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19043.1052 (21H1/May2021Update)
    Intel Core i7-10700 CPU 2.90GHz, 1 CPU, 16 logical and 8 physical cores
    .NET SDK=5.0.301
      [Host]        : .NET Core 3.1.16 (CoreCLR 4.700.21.26205, CoreFX 4.700.21.26205), X64 RyuJIT
      .NET Core 3.1 : .NET Core 3.1.16 (CoreCLR 4.700.21.26205, CoreFX 4.700.21.26205), X64 RyuJIT
    
    
    |      Method |           Job |       Runtime |     Mean |    Error |   StdDev |
    |------------ |-------------- |-------------- |---------:|---------:|---------:|
    |    ForBytes | .NET Core 3.1 | .NET Core 3.1 | 76.95 ns | 0.064 ns | 0.053 ns |
    |    ForArray | .NET Core 3.1 | .NET Core 3.1 | 66.37 ns | 1.258 ns | 1.177 ns |
    | EqualsArray | .NET Core 3.1 | .NET Core 3.1 | 17.91 ns | 0.027 ns | 0.024 ns |
    | EqualsBytes | .NET Core 3.1 | .NET Core 3.1 | 26.26 ns | 0.432 ns | 0.383 ns |

    可以看到,byte[] 比較中,使用了二進(jìn)制對象的方式,耗時(shí)下降了近 60ns,而在 struct 的比較中,耗時(shí)也下降了 40ns。

    在第二種代碼中,我們使用了 Span、切片、 MemoryMarshal、BinaryPrimitives,這些用法都可以給我們的程序性能帶來很大的提升。

    這里示例雖然使用了 Span 等,其最主要是利用了 64位 CPU ,64位 CPU 能夠一次性讀取 8個(gè)字節(jié)(64位),因此我們使用 ReadUInt64BigEndian 一次讀取從字節(jié)數(shù)組中讀取 8 個(gè)字節(jié)去進(jìn)行比較。如果字節(jié)數(shù)組長度為 1024 ,那么第二種方法只需要 比較 128次。

    當(dāng)然,這里并不是這種代碼性能是最強(qiáng)的,因?yàn)?CLR 有很多底層方法具有更猛的性能。不過,我們也看到了,合理使用這些類型,能夠很大程度上提高代碼性能。上面的數(shù)組對比只是一個(gè)簡單的例子,在實(shí)際項(xiàng)目中,我們也可以挖掘更多使用場景。

    更高性能

    雖然第二種方法,快了幾倍,但是性能還不夠強(qiáng)勁,我們可以利用 Span 中的 API,來實(shí)現(xiàn)更快的比較。

            [Benchmark]
            public bool SpanEqual()
            {
                return SpanEqual(_a,_b);
            }
            private bool SpanEqual(byte[] a, byte[] b)
            {
                return a.AsSpan().SequenceEqual(b);
            }

    可以試試

    StructuralComparisons.StructuralEqualityComparer.Equals(a, b);

    性能測試結(jié)果:

    |      Method |           Job |       Runtime |      Mean |     Error |    StdDev |
    |------------ |-------------- |-------------- |----------:|----------:|----------:|
    |    ForBytes | .NET Core 3.1 | .NET Core 3.1 | 77.025 ns | 0.0502 ns | 0.0419 ns |
    |    ForArray | .NET Core 3.1 | .NET Core 3.1 | 66.192 ns | 0.6127 ns | 0.5117 ns |
    | EqualsArray | .NET Core 3.1 | .NET Core 3.1 | 17.897 ns | 0.0122 ns | 0.0108 ns |
    | EqualsBytes | .NET Core 3.1 | .NET Core 3.1 | 25.722 ns | 0.4584 ns | 0.4287 ns |
    |   SpanEqual | .NET Core 3.1 | .NET Core 3.1 |  4.736 ns | 0.0099 ns | 0.0093 ns |

    可以看到,Span.SequenceEqual() 的速度簡直是碾壓。

    以上就是關(guān)于“C#處理類型和二進(jìn)制數(shù)據(jù)轉(zhuǎn)換并提高程序性能的方法”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI