亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

 更新時(shí)間:2022年04月02日 09:40:53   作者:癡者工良  
這篇文章介紹了C#處理類(lèi)型和二進(jìn)制數(shù)據(jù)轉(zhuǎn)換并提高程序性能的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

C# 原語(yǔ)類(lèi)型

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

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

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

  • 整型: sbyte, byte, short, ushort, int, uint, long, ulong
  • 實(shí)數(shù)類(lèi)型: float, double, decimal
  • 字符類(lèi)型: char
  • 布爾類(lèi)型: bool
  • 字符串類(lèi)型: string

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

sizeof(uint);
uint.MaxValue
uint.MinValue

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

<T>() where T : struct
{
}

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

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

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

方法說(shuō)明
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)將指定為長(zhǎng)整型值的一些字節(jié)從內(nèi)存中的一個(gè)地址復(fù)制到另一個(gè)地址。此 API 不符合 CLS。
MemoryCopy(Void, Void, UInt64, UInt64)將指定為無(wú)符號(hào)長(zhǎng)整型值的一些字節(jié)從內(nèi)存中的一個(gè)地址復(fù)制到另一個(gè)地址。此 API 不符合 CLS。
SetByte(Array, Int32, Byte)將指定的值分配給指定數(shù)組中特定位置處的字節(jié)。

下面來(lái)介紹一下 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");

建議復(fù)制代碼自行測(cè)試,斷點(diǎn)調(diào)試,觀察過(guò)程。

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

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

BinaryPrimitives 的實(shí)現(xiàn)原理是 BitConverter,BinaryPrimitives 對(duì) 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)該都是小端在前大端在后的,具體可能會(huì)因處理器架構(gòu)而不同。
你可以使用 BitConverter.IsLittleEndian 來(lái)判斷在當(dāng)前處理器上,C# 程序是大端還是小端在前。

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

以 .Write...() 開(kāi)頭的方法,可以向某個(gè)位置寫(xiě)入數(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);

建議復(fù)制代碼自行測(cè)試,斷點(diǎn)調(diào)試,觀察過(guò)程。

提高代碼安全性

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

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

  • Span 和C#類(lèi)型可以快速安全地訪問(wèn)內(nèi)存。表示任意內(nèi)存的連續(xù)區(qū)域。使用 span 使我們可以序列化為托管.NET數(shù)組,堆棧分配的數(shù)組或非托管內(nèi)存,而無(wú)需使用指針。.NET可以防止緩沖區(qū)溢出。
  • ref struct 、 Span
  • stackalloc 用于創(chuàng)建基于堆棧的數(shù)組。stackalloc 是在需要較小緩沖區(qū)時(shí)避免分配的有用工具。
  • 低級(jí)方法,并在原始類(lèi)型和字節(jié)之間直接轉(zhuǎn)換。MemoryMarshal.GetReference() 、Unsafe.ReadUnaligned() 、Unsafe.WriteUnaligned()
  • BinaryPrimitives具有用于在.NET基本類(lèi)型和字節(jié)之間進(jìn)行有效轉(zhuǎn)換的輔助方法。例如,讀取小尾數(shù)字節(jié)并返回?zé)o符號(hào)的64位數(shù)字。所提供的方法經(jīng)過(guò)了最優(yōu)化,并使用了向量化。BinaryPrimitives.ReadUInt64LittleEndian、BinaryPrimitive

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

        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 可以基元類(lèi)型和 byte 相互轉(zhuǎn)換,例如 int 和 byte 互轉(zhuǎn),或者任意取出、寫(xiě)入基元類(lèi)型的任意一個(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(),它可以將一種基元類(lèi)型的范圍強(qiáng)制轉(zhuǎn)換為另一種基元類(lèi)型的范圍。

        // 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 + ",");
        }

最簡(jiǎn)單的說(shuō)法是,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);

例如,我們要對(duì)比兩個(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í)過(guò) C 語(yǔ)言,應(yīng)該了解 C 語(yǔ)言中的結(jié)構(gòu)體字節(jié)對(duì)齊,在 C# 中也是一樣,兩種類(lèi)型相互轉(zhuǎn)換,除了 C# 結(jié)構(gòu)體轉(zhuǎn) C# 結(jié)構(gòu)體,也可以 C 語(yǔ)言結(jié)構(gòu)體轉(zhuǎn) C# 結(jié)構(gòu)體,但是要考慮好字節(jié)對(duì)齊,如果兩個(gè)結(jié)構(gòu)體所占用的內(nèi)存大小不一樣,則可能在轉(zhuǎn)換時(shí)出現(xiàn)數(shù)據(jù)丟失或出現(xiàn)錯(cuò)誤。

4,Marshal

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

例如,來(lái)確定 C# 中的一些類(lèi)型大?。?/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)存問(wèn)題異常問(wèn)題,需要轉(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)制處理類(lèi),可以在很多方面提升代碼性能,在前面的學(xué)習(xí)中,我們大概了解這些對(duì)象,但是有什么應(yīng)用場(chǎng)景?真的能夠提升性能?有沒(méi)有練習(xí)代碼?

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

        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è)代碼很簡(jiǎn)單,循環(huán)遍歷字節(jié)數(shù)組,一個(gè)個(gè)判斷是否相等。

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

        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;
        }

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

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 的測(cè)試結(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)制對(duì)象的方式,耗時(shí)下降了近 60ns,而在 struct 的比較中,耗時(shí)也下降了 40ns。

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

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

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

更高性能

雖然第二種方法,快了幾倍,但是性能還不夠強(qiáng)勁,我們可以利用 Span 中的 API,來(lái)實(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);

性能測(cè)試結(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() 的速度簡(jiǎn)直是碾壓。
對(duì)于 C# 中的二進(jìn)制處理技巧就介紹到這里,閱讀 CLR 源碼 時(shí),我們可以學(xué)習(xí)到很多騷操作,讀者可以多閱讀 CLR 源碼,對(duì)技術(shù)提升有很大的幫助。

到此這篇關(guān)于C#處理類(lèi)型和二進(jìn)制數(shù)據(jù)轉(zhuǎn)換并提高程序性能的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論