1 文章范圍


          本文將.netcore新出現(xiàn)的與Buffer操作相關(guān)的類型進行簡單分析與講解,由于資料有限,一些見解為個人見解,可能不是很準(zhǔn)確。這些新類型將包括BinaryPrimitives、Span<>,Memory<>,ArrayPool<>,Memorypool<>

          2 BinaryPrimitives

          在網(wǎng)絡(luò)傳輸中,最小單位是byte,很多場景,我們需要將int long
          short等類型與byte[]相互轉(zhuǎn)換。比如,將int轉(zhuǎn)換為BigEndian的4個字節(jié),在過去,我們很容易就想到BitConverter,但BitConverter設(shè)計得不夠好友,BitConverter.GetBytes(int
          value)得到的byte[]的字節(jié)順序永遠與主機的字節(jié)順序一樣,我們不得不再根據(jù)BitConverter的IsLittleEndian屬性判斷是否需要對得到byte[]進行轉(zhuǎn)換字節(jié)順序,而BinaryPrimitives的Api設(shè)計為嚴(yán)格區(qū)分Endian,每個Api都指定了目標(biāo)Endian。

          BitConverter
          var intValue = 1; var bytes = BitConverter.GetBytes(intValue); if
          (BitConverter.IsLittleEndian == true) { Array.Reverse(bytes); }
          BinaryPrimitives
          var intValue = 1; var bytes = new byte[sizeof(int)];
          BinaryPrimitives.WriteInt32BigEndian(bytes, intValue);
          3 Span<>

          Span是一個高效的連續(xù)內(nèi)存范圍操作值類型,我們知道Array
          是一個連接的內(nèi)存范圍的引用類型,那為什么還需要Span類型呢?可以簡單這么認(rèn)為:Span除了提供更高性能的Array的讀寫功能之外,還提供了比ArraySegment更易于理解和使用的內(nèi)存局部視圖,也就是說Span功能包含了Array+ArraySegment的功能,我可以使用BenchmarkDotNet對比Span、Array和指針讀寫一個連接內(nèi)存的性能比較,測試結(jié)果為Span>Pointer>Array:

          讀寫代碼
          public class DemoContext { private byte[] array = new byte[1024]; [Benchmark]
          public void ByteArray() { for (var i = 0; i < array.Length; i++) { array[i] =
          array[i]; } } [Benchmark] public void ByteSpan() { var span = array.AsSpan();
          for (var i = 0; i < span.Length; i++) { span[i] = span[i]; } } [Benchmark]
          unsafe public void BytePointer() { fixed (byte* pointer = &array[0]) { for (var
          i = 0; i < array.Length; i++) { *(pointer + i) = *(pointer + i); } } } }
          Benchmark報告
          | Method | Mean | Error | StdDev | |------------
          |---------:|--------:|--------:| | ByteArray | 577.4 ns | 9.07 ns | 8.48 ns | |
          ByteSpan | 323.8 ns | 0.87 ns | 0.81 ns | | BytePointer | 499.4 ns | 4.09 ns |
          3.82 ns |
          Memory<>


          如果嘗試將Span<>作為全局變量,或在異步方法聲明為變量,你會得到編譯器的錯誤,原因不在本文講解范圍內(nèi),而Memory<>類型可以滿足這些需求,Memory<>提供了用于數(shù)據(jù)讀寫的Span屬性,這個Span屬性是每將獲取時都有一些計算,所以我們應(yīng)該盡量避免多次獲取它的Span屬性。

          合理的獲取Span
          var span = memory.Span; for (var i = 0; i < span.Length; i++) { span[i] =
          span[i]; }
          不合理的獲取Span
          for (var i = 0; i < memory.Length; i++) { memory.Span[i] = memory.Span[i]; }
          Benchmark報告
          | Method | Mean | Error | StdDev | |------------
          |-----------:|---------:|---------:| | ByteMemory1 | 325.8 ns | 1.03 ns | 0.97
          ns | | ByteMemory2 | 3,344.9 ns | 11.91 ns | 11.14 ns |
          ArrayPool<>


          ArrayPool<>用于解決頻繁申請內(nèi)存和釋放內(nèi)存導(dǎo)致GC壓力過大的場景,比如System.Text.Json在序列對象時為utf8的byte[]時,事先是無法計算最終byte[]的長度的,過程中可能要不斷申請和調(diào)整緩沖區(qū)的大小。在沒有ArrayPool加持的情況下,高頻次的序列化,則會生產(chǎn)高頻創(chuàng)建byte[]的過程,隨之GC壓力也會增大。ArrayPool的設(shè)計邏輯是,從pool申請一個指定最小長度的緩沖區(qū),緩沖區(qū)在不需要的時候,將其返回到pool里,待以重復(fù)利用。
          var pool = ArrayPool<byte>.Shared; var buffer = pool.Rent(1024); // 開始利用buffer
          // ... // 使用結(jié)束 pool.Return(buffer);
          Rent用于申請,實際上是租賃,Return是歸還,返回到池中。我們可以使用IDisposable接口來包裝Return功能,使用上更方便一些:
          /// <summary> /// 定義數(shù)組持有者的接口 /// </summary> /// <typeparam
          name="T"></typeparam> public interface IArrayOwner<T> : IDisposable { ///
          <summary> /// 獲取持有的數(shù)組 /// </summary> T[] Array { get; } /// <summary> ///
          獲取數(shù)組的有效長度 /// </summary> int Count { get; } } /// <summary> /// 表示共享的數(shù)組池 ///
          </summary> public static class ArrayPool { /// <summary> /// 租賃數(shù)組 ///
          </summary> /// <typeparam name="T">元素類型</typeparam> /// <param
          name="minLength">最小長度</param> /// <returns></returns> public static
          IArrayOwner<T> Rent<T>(int minLength) { return new ArrayOwner<T>(minLength); }
          /// <summary> /// 表示數(shù)組持有者 /// </summary> /// <typeparam name="T"></typeparam>
          [DebuggerDisplay("Count = {Count}")]
          [DebuggerTypeProxy(typeof(ArrayOwnerDebugView<>))] private class ArrayOwner<T>
          :IDisposable, IArrayOwner<T> { /// <summary> /// 獲取持有的數(shù)組 /// </summary> public
          T[] Array { get; } /// <summary> /// 獲取數(shù)組的有效長度 /// </summary> public int Count
          { get; } /// <summary> /// 數(shù)組持有者 /// </summary> /// <param
          name="minLength"></param> public ArrayOwner(int minLength) { this.Array =
          ArrayPool<T>.Shared.Rent(minLength); this.Count = minLength; } /// <summary>
          /// 歸還數(shù)組 /// </summary> Public void Dispose() {
          ArrayPool<T>.Shared.Return(this.Array); } } /// <summary> /// 調(diào)試視圖 ///
          </summary> /// <typeparam name="T"></typeparam> private class
          ArrayOwnerDebugView<T> { [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
          public T[] Items { get; } /// <summary> /// 調(diào)試視圖 /// </summary> /// <param
          name="owner"></param> public ArrayOwnerDebugView(IArrayOwner<T> owner) {
          this.Items = owner.Array.AsSpan(0, owner.Count).ToArray(); } } }
          改造之后的使用
          using var buffer = ArrayPool.Rent<byte>(1024); // 盡情的使用buffer吧,自動回收
          Memorypool<>


          Memorypool<>本質(zhì)上還是使用了ArrayPool<>,Memorypool只提供了Rent功能,返回一個IMomoryOwner<>,對其Dispose等同于Return過程,使用方式和我們上面改造過的ArrayPool靜態(tài)類的使用方式是一樣的。

          MemoryMarshal靜態(tài)類


          MemoryMarshal是一個工具類,類似于我們指針操作時常常用到的Marshal類,它操作一些更底層的Span或Memory操作,比如提供將不同基元類型的Span相互轉(zhuǎn)換等。

          獲取Span的指針
          var span = new Span<byte>(new byte[] { 1, 2, 3, 4 }); ref var p0 = ref
          MemoryMarshal.GetReference(span); fixed (byte* pointer = &p0) {
          Debug.Assert(span[0] == *pointer); }
          Span泛型參數(shù)類型轉(zhuǎn)換
          Span<int> intSpan = new Span<int>(new int[] { 1024 }); Span<byte> byteSpan =
          MemoryMarshal.AsBytes(intSpan);
          ReadonlyMemory<>轉(zhuǎn)換為Memory
          // 相當(dāng)于給ReadonlyMemory移除只讀功能 Memory<T>
          MemoryMarshal.AsMemory<T>(ReadonlyMemory<T> readonly)

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點信息
          問題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                色婷婷国产精品秘 免费网站 | 日韩精品久久久久久久 | 亚洲 成人 综合 另类 | 国产精品久久久免费 | 爆操小美女 |