<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      NativeBuferring,一種零分配的數(shù)據(jù)類型[上篇]

      之前一個(gè)項(xiàng)目涉及到針對(duì)海量(千萬(wàn)級(jí))實(shí)時(shí)變化數(shù)據(jù)的計(jì)算,由于對(duì)性能要求非常高,我們不得不將參與計(jì)算的數(shù)據(jù)存放到內(nèi)存中,并通過(guò)檢測(cè)數(shù)據(jù)存儲(chǔ)的變化實(shí)時(shí)更新內(nèi)存的數(shù)據(jù)。存量的數(shù)據(jù)幾乎耗用了上百G的內(nèi)存,再加上它們?cè)诿總€(gè)時(shí)刻都在不斷地變化,所以每時(shí)每刻都無(wú)數(shù)的對(duì)象被創(chuàng)建出來(lái)(添加+修改),同時(shí)無(wú)數(shù)現(xiàn)有的對(duì)象被“廢棄”(刪除+修改)。這種情況針對(duì)GC的壓力可想而知,所以每當(dāng)進(jìn)行一次2代GC的時(shí)候,計(jì)算的耗時(shí)總會(huì)出現(xiàn)“抖動(dòng)”。為了解決這類問(wèn)題,幾天前嘗試著創(chuàng)建了一個(gè)名為NativeBuffering的框架。目前這個(gè)框架遠(yuǎn)未成熟,而且是一種“時(shí)間換空間”的解決方案,雖然徹底解決了內(nèi)存分配的問(wèn)題,但是以犧牲數(shù)據(jù)讀取性能為代價(jià)的。這篇文章只是簡(jiǎn)單介紹一下NativeBuffering的設(shè)計(jì)原理和用法,并順便收集一下大家的建議。[本文演示源代碼從這里下載]

      一、讓對(duì)象映射一段連續(xù)的內(nèi)存
      二、Unmanaged類型
      三、BufferedBinary類型
      四、BufferedString類型

      一、讓對(duì)象映射一段連續(xù)的內(nèi)存

      針對(duì)需要高性能的互聯(lián)網(wǎng)應(yīng)用來(lái)說(shuō),GC針對(duì)性能的影響是不得不考慮的,減少GC影響最根本的解決方案就是“不需要GC”。如果一個(gè)對(duì)象占據(jù)的內(nèi)存是“連續(xù)的”,并且承載該對(duì)象的字節(jié)數(shù)是可知的,那么我們就可以使用一個(gè)預(yù)先創(chuàng)建的字節(jié)數(shù)組來(lái)存儲(chǔ)數(shù)據(jù)對(duì)象。我們進(jìn)一步采用“對(duì)象池”的方式來(lái)管理這些字節(jié)數(shù)組,那么就能實(shí)現(xiàn)真正意義上的“零分配”,自然也就不會(huì)帶來(lái)任何的GC壓力。不僅如此,連續(xù)的內(nèi)存布局還能充分地利用各級(jí)緩存,對(duì)提高性能來(lái)說(shuō)是一個(gè)加分項(xiàng)。如果從序列化/發(fā)序列話角度來(lái)說(shuō),這樣的實(shí)現(xiàn)直接省去了反序列化的過(guò)程。

      但是我們知道在托管環(huán)境這一前提是不成立的,只有值類型的對(duì)象映射一片連續(xù)的內(nèi)存。對(duì)于引用類型的對(duì)象來(lái)說(shuō),只有值類型的字段將自身的值存儲(chǔ)在該對(duì)象所在的內(nèi)存區(qū)域,對(duì)于引用類型的字段來(lái)說(shuō),存儲(chǔ)的僅僅目標(biāo)對(duì)象的地址而已,所以“讓對(duì)象映射一段連續(xù)內(nèi)存”是沒(méi)法做到的。但是基元類型結(jié)構(gòu)體默認(rèn)采用這樣的內(nèi)存布局,所以我們可以采用“非托管或者Unsafe”的方式將它們映射到我們構(gòu)建的一段字節(jié)序列。對(duì)于一個(gè)只包含基元類型和結(jié)構(gòu)體成員的“復(fù)合”類型來(lái)說(shuō),對(duì)應(yīng)實(shí)例的所有數(shù)據(jù)成員可以存儲(chǔ)到一段連續(xù)的字節(jié)序列中。

      既然如此,我們就可以設(shè)計(jì)這樣一種數(shù)據(jù)類型:它不在使用“字段”來(lái)定義其數(shù)據(jù)成員,而將所有的數(shù)據(jù)成員轉(zhuǎn)換成一段字節(jié)序列。我們?yōu)槊總€(gè)成員定義一個(gè)屬性將數(shù)據(jù)讀出來(lái),這相當(dāng)于實(shí)現(xiàn)了“將對(duì)象映射為一段連續(xù)內(nèi)存”的目標(biāo)。以此類推,任何一個(gè)數(shù)據(jù)類型其實(shí)都可以通過(guò)這樣的策略實(shí)現(xiàn)”連續(xù)內(nèi)存布局“。

      正如上面提到過(guò)的,這是一種典型的”時(shí)間換空間“的解決方案,所以NativeBuffering的一個(gè)目標(biāo)就是盡可能地提高讀取數(shù)據(jù)成員的性能,其中一個(gè)主要的途徑就是Buffer存儲(chǔ)的字節(jié)就是數(shù)據(jù)類型原生(Native)的表現(xiàn)形式。也就是說(shuō)原生的數(shù)據(jù)類型采用怎樣的內(nèi)存布局,NativeBuffering就采用怎樣的布局,這也是NativeBuffering名稱的由來(lái)。在這一根本前提下,NativeBuffering針對(duì)單一數(shù)據(jù)的讀取并沒(méi)有性能損失,因?yàn)橹虚g不存在任何Marshal的過(guò)程,針對(duì)影響讀取性能的因素是需要額外計(jì)算待讀取數(shù)據(jù)在Buffer中的偏移量。

      也正是為了保證“與數(shù)據(jù)類型的Native形式保持一直”,NativeBuffering對(duì)于數(shù)據(jù)類型做了限制。總地來(lái)說(shuō),NativeBuffering只支持UnmanagedBufferedBinaryBufferedString三種基本類型。NativeBuffering將定義的數(shù)據(jù)類型稱為BufferedMessage,除了上述三種基本的數(shù)據(jù)類型,BufferedMessage的數(shù)據(jù)類型還可以是另一個(gè)BufferedMessage類型,以及基于這四種類型的集合和字典。下面的內(nèi)容主要從“內(nèi)存布局”的角度介紹上述三種基本的數(shù)據(jù)類型,同時(shí)通過(guò)實(shí)例演示其基本用法。

      二、Unmanaged類型

      顧名思義,Unmanaged類型可以理解為不涉及托管對(duì)象引用的值類型(可以參與我們的文章《.NET的基元類型包括哪些?Unmanaged和Blittable類型又是什么?》),如下的類型屬于Unmanaged 類型的范疇。由于這樣的類型在托管和非托管環(huán)境的內(nèi)存布局是完全一致的,所以可以使用靜態(tài)類型Unsafe從指定的地址指針將值直接讀取出來(lái)。

      • 14種基元類型+Decimal(decimal)

      • 枚舉類型

      • 指針類型(比如int*, long*)

      • 只包含Unmanaged類型字段的結(jié)構(gòu)體

      我們創(chuàng)建一個(gè)簡(jiǎn)單的控制臺(tái)程序演示NativeBuffering的基本用法。NativeBuffering除了提供同名的NuGet包外,還提供了一個(gè)名為NativeBuffering.Generator的NuGet包,后者以Source Generator的形式根據(jù)“原類型”生成對(duì)應(yīng)的BufferedMessage類型,并生成用來(lái)計(jì)算字節(jié)數(shù)量和輸出字節(jié)內(nèi)容的代碼。我們定義了如下這個(gè)Entity類作為“源類型”(上面標(biāo)注了BufferedMessageSourceAttribute特性),由于我們還需要為該類型生成一些額外成員,所以必須將其定義成partial類。

      [BufferedMessageSource]
      public partial class Entity
      {
          public long Foo { get; set; }
          public UnmanagedStruct Bar { get; set; }
      }
      
      public readonly record struct UnmanagedStruct(int X, double Y);

      如上面的代碼片段所示,Entity具有Foo和Bar兩個(gè)數(shù)據(jù)成員,類型分別為long(Int64)和UnmanagedStruct ,它們都是Unmanaged類型。如果將這個(gè)Entity轉(zhuǎn)換成對(duì)應(yīng)的BufferedMessage,承載字節(jié)將具有如下的結(jié)構(gòu)。任何一個(gè)BufferedMessage對(duì)象承載的字節(jié)都存儲(chǔ)在一個(gè)預(yù)先創(chuàng)建的字節(jié)數(shù)組中。如果它具有N個(gè)成員(被稱為字段),前N * 4個(gè)字節(jié)用來(lái)存儲(chǔ)一個(gè)整數(shù)指向?qū)?yīng)成員的起始位置(在字節(jié)數(shù)組中的索引),后續(xù)的字節(jié)依次存儲(chǔ)每個(gè)數(shù)據(jù)成員。在讀取某個(gè)成員的時(shí)候,先根據(jù)字段索引讀取目標(biāo)內(nèi)容在緩沖區(qū)中的位置,然后根據(jù)類型讀取對(duì)應(yīng)的值。

      image

      有人可能說(shuō),既然值類型的長(zhǎng)度都是固定的,完全可以按照下圖(上)所示的方式直接以“平鋪”的方式存儲(chǔ)每個(gè)字段的值,然后根據(jù)數(shù)據(jù)類型確定具體字段的初始位置。實(shí)際上最初我也是這么設(shè)計(jì)的,但是如果考慮內(nèi)存地址對(duì)齊下圖(下),針對(duì)字段初始位置的計(jì)算就比較麻煩。內(nèi)存對(duì)齊目前尚未實(shí)現(xiàn),實(shí)現(xiàn)了之后相信對(duì)性能有較大的提升。

      image

      具有上述結(jié)構(gòu)的字節(jié)不可能手工生成,所以我們采用了Source Generator的方式。安裝的Source Generator(NativeBuffering.Generator)將會(huì)幫助我們生成如下圖所示的兩個(gè).cs文件。

      image

      Entity.g.cs補(bǔ)上上了Entity這個(gè)partial類余下的部分。如下面的代碼片段所示,自動(dòng)生成的代碼讓這個(gè)類實(shí)現(xiàn)了IBufferedObjectSource接口,實(shí)現(xiàn)的CalculateSize用于計(jì)算生成的字節(jié)數(shù),而具體的字節(jié)輸出則實(shí)現(xiàn)在Writer方法中。

      public partial class Entity : IBufferedObjectSource
      {
          public int CalculateSize()
          {
              var size = 0;
              size += NativeBuffering.Utilities.CalculateUnmanagedFieldSize(Foo);
              size += NativeBuffering.Utilities.CalculateUnmanagedFieldSize(Bar);
              return size;
          }
          public void Write(BufferedObjectWriteContext context)
          {
              using var scope = new BufferedObjectWriteContextScope(context);
              scope.WriteUnmanagedField(Foo);
              scope.WriteUnmanagedField(Bar);
          }
      }

      NativeBuffering.Generator還幫助我們自動(dòng)生成對(duì)應(yīng)的EntityBufferedMessage 類型。如下面的代碼片段所示,為了盡可能節(jié)省內(nèi)存,我們將其定義為只讀的結(jié)構(gòu)體,并實(shí)現(xiàn)了IReadOnlyBufferedObject<EntityBufferedMessage> 接口。EntityBufferedMessage是對(duì)一個(gè)NativeBuffer對(duì)象的封裝,NativeBuffer是一個(gè)核心類型,用來(lái)表示從指定位置開(kāi)始的一段緩沖區(qū)。它Bytes屬性表示作為緩存區(qū)的字節(jié)數(shù)組,Start屬性表示起始地址的指針。至于兩個(gè)屬性Foo和Bar返回的值,分別調(diào)用相應(yīng)的方法從這個(gè)NativeBuffer對(duì)象中讀取出來(lái)。

      public unsafe readonly struct EntityBufferedMessage : IReadOnlyBufferedObject<EntityBufferedMessage>
      {
          public NativeBuffer Buffer { get; }
          public EntityBufferedMessage(NativeBuffer buffer) => Buffer = buffer;
          public static EntityBufferedMessage Parse(NativeBuffer buffer) => new EntityBufferedMessage(buffer);
          public System.Int64 Foo => Buffer.ReadUnmanagedField<System.Int64>(0);
          public ref readonly UnmanagedStruct Bar => ref Buffer.ReadUnmanagedFieldAsRef<UnmanagedStruct>(1);
      }
      
      public interface IReadOnlyBufferedObject<T> where T: IReadOnlyBufferedObject<T>
      {
          static abstract T Parse(NativeBuffer buffer);
      }
      
      public unsafe readonly struct  NativeBuffer
      {
          public byte[] Bytes { get; }
          public void* Start { get; }
          ...
      }

      由于UnmanagedStruct 是一個(gè)自定義的結(jié)構(gòu)體,我們知道值類型賦值采用“拷貝”的方式。如果這個(gè)結(jié)構(gòu)體包含過(guò)多的成員,可能會(huì)因?yàn)榭截惖淖止?jié)過(guò)多而帶來(lái)性能問(wèn)題,為此我直接返回這個(gè)結(jié)構(gòu)體的引用。由于整個(gè)BufferedMessage 是只讀的,所以返回的引用也是只讀的。為了方便BufferedMessage對(duì)象的創(chuàng)建,我們?yōu)閷?shí)現(xiàn)的IReadOnlyBufferedObject<EntityBufferedMessage>接口定義了一個(gè)靜態(tài)方法Parse。如下的程序驗(yàn)證了EntityBufferedMessage 與原始Entity類的“等效性”。

      using NativeBuffering;
      using System.Diagnostics;
      
      var entity = new Entity
      {
          Foo = 123,
          Bar = new UnmanangedStruct(789, 3.14)
      };
      
      var bytes = new byte[entity.CalculateSize()];
      var context = new BufferedObjectWriteContext(bytes);
      entity.Write(context);
      File.WriteAllBytes(".data", bytes);
      
      EntityBufferedMessage bufferedMessage;
      BufferOwner? bufferOwner = null;
      
      try
      {
          using (var fs = new FileStream(".data", FileMode.Open))
          {
              var byteCount = (int)fs.Length;
              bufferOwner = BufferPool.Rent(byteCount);
              fs.Read(bufferOwner.Bytes, 0, byteCount);
          }
      
          bufferedMessage = BufferedMessage.Create<EntityBufferedMessage>(ref bufferOwner);
          Debug.Assert(bufferedMessage.Foo == 123);
          Debug.Assert(bufferedMessage.Bar.X == 789);
          Debug.Assert(bufferedMessage.Bar.Y == 3.14);
      }
      finally
      {
          bufferOwner?.Dispose();
      }

      整個(gè)演示程序分兩個(gè)部分,第一個(gè)部分演示了如何將一個(gè)Entity對(duì)象轉(zhuǎn)換成我們需要的字節(jié),并持久化到一個(gè)文件中。第二部分演示如何讀取字節(jié)并生成對(duì)應(yīng)的EntityBufferedMessage,這里我們使用了“緩沖池”,所以針對(duì)EntityBufferedMessage的創(chuàng)建不會(huì)涉及內(nèi)存分配。我們沒(méi)有直接使用ArrayPool<byte>,因?yàn)閿?shù)據(jù)成員根據(jù)指針讀取,我們需要保證整個(gè)緩沖區(qū)不會(huì)因GC的“壓縮”而移動(dòng)位置,通過(guò)BufferPool實(shí)現(xiàn)的內(nèi)存池將字節(jié)數(shù)組存儲(chǔ)在POH中,位置永遠(yuǎn)不會(huì)改變。

      三、BufferedBinary類型

      BufferedBinary 是NativeBuffering支持的第二種基本類型,它表示一個(gè)長(zhǎng)度確定的字節(jié)序列。和Unmanaged類型不同,這是一種長(zhǎng)度可變的類型,所以我們使用前置的4字節(jié)以整數(shù)的形式表示字節(jié)長(zhǎng)度。BufferedBinary 被定義成如下這樣一個(gè)結(jié)構(gòu)體,它同樣實(shí)現(xiàn)了IReadOnlyBufferedObject<BufferedBinary>接口。我們可以調(diào)用AsSpan方法以ReadOnlySpan<byte>的形式字節(jié)序列。

      public unsafe readonly struct BufferedBinary : IReadOnlyBufferedObject<BufferedBinary>
      {
          public BufferedBinary(NativeBuffer buffer) => Buffer = buffer;
          public NativeBuffer Buffer { get; }
          public int Length => Unsafe.Read<int>(Buffer.Start);
          public ReadOnlySpan<byte> AsSpan() => new(Buffer.GetPointerByOffset(sizeof(int)), Length);
          public static BufferedBinary Parse(NativeBuffer buffer) => new(buffer);
      }

      為了演示字節(jié)序列在NativeBuffering中的應(yīng)用,我們?yōu)镋ntity類添加了如下這個(gè)字節(jié)數(shù)組類型的屬性Baz。

      [BufferedMessageSource]
      public partial class Entity
      {
          public long Foo { get; set; }
          public UnmanagedStruct Bar { get; set; }
          public byte[] Baz { get; set; }
      }

      新的Entity對(duì)應(yīng)的BufferedMessage將具有如下的內(nèi)存布局。

      image

      Entity類的定義一旦放生改變,NativeBuffering.Generator將自動(dòng)修正生成的兩個(gè).cs文件的內(nèi)容。

      [BufferedMessageSource]
      public partial class Entity
      {
          public long Foo { get; set; }
          public UnmanagedStruct Bar { get; set; }
          public byte[] Baz { get; set; }
      }
      
      public partial class Entity : IBufferedObjectSource
      {
          public int CalculateSize()
          {
              var size = 0;
              size += NativeBuffering.Utilities.CalculateUnmanagedFieldSize(Foo);
              size += NativeBuffering.Utilities.CalculateUnmanagedFieldSize(Bar);
              size += NativeBuffering.Utilities.CalculateBinaryFieldSize(Baz);
              return size;
          }
          public void Write(BufferedObjectWriteContext context)
          {
              using var scope = new BufferedObjectWriteContextScope(context);
              scope.WriteUnmanagedField(Foo);
              scope.WriteUnmanagedField(Bar);
              scope.WriteBinaryField(Baz);
          }
      }
      
      public unsafe readonly struct EntityBufferedMessage : IReadOnlyBufferedObject<EntityBufferedMessage>
      {
          public NativeBuffer Buffer { get; }
          public EntityBufferedMessage(NativeBuffer buffer) => Buffer = buffer;
          public static EntityBufferedMessage Parse(NativeBuffer buffer) => new EntityBufferedMessage(buffer);
          public System.Int64 Foo => Buffer.ReadUnmanagedField<System.Int64>(0);
          public ref UnmanagedStruct Bar => ref Buffer.ReadUnmanagedFieldAsRef<UnmanagedStruct>(1);
          public BufferedBinary Baz => Buffer.ReadBufferedObjectField<BufferedBinary>(2);
      }

      在如下所示的演示程序中,通過(guò)Entity的Baz屬性設(shè)置的字節(jié)數(shù)組,在生成的EntityBufferedMessage對(duì)象中,同樣可以利用同名的屬性讀取出來(lái)。

      using NativeBuffering;
      using System.Diagnostics;
      
      var entity = new Entity
      {
          Foo = 123,
          Bar = new UnmanangedStruct(789, 3.14),
          Baz = new byte[] { 1, 2, 3 }
      };
      
      var bytes = new byte[entity.CalculateSize()];
      var context = new BufferedObjectWriteContext(bytes);
      entity.Write(context);
      File.WriteAllBytes(".data", bytes);
      
      EntityBufferedMessage bufferedMessage;
      BufferOwner? bufferOwner = null;
      
      try
      {
          using (var fs = new FileStream(".data", FileMode.Open))
          {
              var byteCount = (int)fs.Length;
              bufferOwner = BufferPool.Rent(byteCount);
              fs.Read(bufferOwner.Bytes, 0, byteCount);
          }
      
          bufferedMessage = BufferedMessage.Create<EntityBufferedMessage>(ref bufferOwner);
          Debug.Assert(bufferedMessage.Foo == 123);
          Debug.Assert(bufferedMessage.Bar.X == 789);
          Debug.Assert(bufferedMessage.Bar.Y == 3.14);
      
          Debug.Assert(bufferedMessage.Baz.Length == 3);
          var byteSpan = bufferedMessage.Baz.AsSpan();
          Debug.Assert(byteSpan[0] == 1);
          Debug.Assert(byteSpan[1] == 2);
          Debug.Assert(byteSpan[2] == 3);
      }
      finally
      {
          bufferOwner?.Dispose();
      }

      四、BufferedString類型

      字符串同樣是一個(gè)“長(zhǎng)度可變”數(shù)據(jù)類型。如果將一個(gè)字符串轉(zhuǎn)換成一個(gè)一段連續(xù)的字節(jié)呢?可能很多人會(huì)說(shuō),那還不容易,將其編碼不久可以了嗎?確實(shí)沒(méi)錯(cuò),但是如何將編碼轉(zhuǎn)換成字符串呢?解碼嗎?不要忘了我們的目標(biāo)是“創(chuàng)建一個(gè)完全無(wú)內(nèi)存分配”的數(shù)據(jù)類型。當(dāng)我們解碼字節(jié)將其“還原”一個(gè)字符串時(shí),實(shí)際上CLR會(huì)創(chuàng)建一個(gè)String類型(引用類型)的實(shí)例,并將指定的字節(jié)轉(zhuǎn)換成標(biāo)準(zhǔn)的字符字節(jié)(采用UTF-16編碼)并將其拷貝到實(shí)例所在的內(nèi)存區(qū)域。

      要達(dá)到我們“無(wú)分配”的目標(biāo),字符串轉(zhuǎn)換的字節(jié)序列必須與這個(gè)String實(shí)例在內(nèi)存中的內(nèi)容完全一致。此時(shí)你不了解字符串對(duì)象在.NET中的內(nèi)存布局,可以參閱我的另一篇文章《你知道.NET的字符串在內(nèi)存中是如何存儲(chǔ)的嗎?》。總的來(lái)說(shuō),一個(gè)字符串實(shí)例由ObjHeader+TypeHandle+Length+Encoded Characters4部分組成。我們還需要知道整個(gè)字節(jié)序列的長(zhǎng)度,所以我們還需要前置的4個(gè)字節(jié)。

      字符串在NativeBuffering通過(guò)如下這個(gè)名為BufferedString的結(jié)構(gòu)體表示,它同樣實(shí)現(xiàn)了IReadOnlyBufferedObject<BufferedString>接口。BufferedString可以通過(guò)AsString方法轉(zhuǎn)換成String類型,該方法不會(huì)帶來(lái)任何的內(nèi)存分配。AsString方法用在針對(duì)String的隱式類型轉(zhuǎn)換操作符上,所以在任何使用到String類型的地方都可以直接使用BufferedString類型

      public unsafe readonly struct BufferedString : IReadOnlyBufferedObject<BufferedString>
      {
          private readonly void* _start;
          public BufferedString(NativeBuffer buffer) => _start = buffer.Start;
          public BufferedString(void* start)=> _start = start;
          public static BufferedString Parse(NativeBuffer buffer) => new(buffer);
          public static BufferedString Parse(void* start) => new(start);
          public static int CalculateSize(void* start) => Unsafe.Read<int>(start);
          public string AsString()
          {
              string v = default!;
              Unsafe.Write(Unsafe.AsPointer(ref v), new IntPtr(Unsafe.Add<byte>(_start, sizeof(int) + IntPtr.Size)));
              return v;
          }
          public static implicit operator string(BufferedString value) => value.AsString();
          public override string ToString() => AsString();
      }

      為了演示字符串在NativeBuffering中的應(yīng)用,我們?yōu)镋ntity添加了字符串類型的Qux屬性。

      [BufferedMessageSource]
      public partial class Entity
      {
          public long Foo { get; set; }
          public UnmanagedStruct Bar { get; set; }
          public byte[] Baz { get; set; }
          public string Qux { get; set; }
      }

      對(duì)于新的Entity類型,它對(duì)應(yīng)的BufferedMessage封裝的字節(jié)序列將變成如下的結(jié)構(gòu)。

      image

      在Entity添加的Qux屬性,也將同步體現(xiàn)在生成的兩個(gè).cs文件中。

      public partial class Entity : IBufferedObjectSource
      {
          public int CalculateSize()
          {
               var size = 0;
              size += NativeBuffering.Utilities.CalculateUnmanagedFieldSize(Foo);
              size += NativeBuffering.Utilities.CalculateUnmanagedFieldSize(Bar);
              size += NativeBuffering.Utilities.CalculateBinaryFieldSize(Baz);
              size += NativeBuffering.Utilities.CalculateStringFieldSize(Qux);
              return size;
          }
          public void Write(BufferedObjectWriteContext context)
          {
              using var scope = new BufferedObjectWriteContextScope(context);
              scope.WriteUnmanagedField(Foo);
              scope.WriteUnmanagedField(Bar);
              scope.WriteBinaryField(Baz);
              scope.WriteStringField(Qux);
          }
      }
      
      public unsafe readonly struct EntityBufferedMessage : IReadOnlyBufferedObject<EntityBufferedMessage>
      {
          public NativeBuffer Buffer { get; }
          public EntityBufferedMessage(NativeBuffer buffer) => Buffer = buffer;
          public static EntityBufferedMessage Parse(NativeBuffer buffer) => new EntityBufferedMessage(buffer);
          public System.Int64 Foo => Buffer.ReadUnmanagedField<System.Int64>(0);
          public ref UnmanagedStruct Bar => ref Buffer.ReadUnmanagedFieldAsRef<UnmanagedStruct>(1);
          public BufferedBinary Baz => Buffer.ReadBufferedObjectField<BufferedBinary>(2);
          public BufferedString Qux => Buffer.ReadBufferedObjectField<BufferedString>(3);
      }

      我們同樣在演示程序中添加了針對(duì)字符串?dāng)?shù)據(jù)成員的驗(yàn)證。

      using NativeBuffering;
      using System.Diagnostics;
      
      var entity = new Entity
      {
          Foo = 123,
          Bar = new UnmanangedStruct(789, 3.14),
          Baz = new byte[] { 1, 2, 3 },
          Qux = "Hello, World!"
      };
      
      var bytes = new byte[entity.CalculateSize()];
      var context = new BufferedObjectWriteContext(bytes);
      entity.Write(context);
      File.WriteAllBytes(".data", bytes);
      
      EntityBufferedMessage bufferedMessage;
      BufferOwner? bufferOwner = null;
      
      try
      {
          using (var fs = new FileStream(".data", FileMode.Open))
          {
              var byteCount = (int)fs.Length;
              bufferOwner = BufferPool.Rent(byteCount);
              fs.Read(bufferOwner.Bytes, 0, byteCount);
          }
      
          bufferedMessage = BufferedMessage.Create<EntityBufferedMessage>(ref bufferOwner);
          Debug.Assert(bufferedMessage.Foo == 123);
          Debug.Assert(bufferedMessage.Bar.X == 789);
          Debug.Assert(bufferedMessage.Bar.Y == 3.14);
          Debug.Assert(bufferedMessage.Baz.Length == 3);
      
          var byteSpan = bufferedMessage.Baz.AsSpan();
          Debug.Assert(byteSpan[0] == 1);
          Debug.Assert(byteSpan[1] == 2);
          Debug.Assert(byteSpan[2] == 3);
      
          Debug.Assert(bufferedMessage.Qux == "Hello, World!");
      }
      finally
      {
          bufferOwner?.Dispose();
      }

      上篇主要介紹NativeBuffering的三種基本的數(shù)據(jù)類型,下面我們接著介紹它對(duì)“集合”和“字典”的支持!

      posted @ 2023-07-31 08:31  Artech  閱讀(2491)  評(píng)論(9)    收藏  舉報(bào)
      主站蜘蛛池模板: 亚洲精品天堂一区二区| 中文字幕结果国产精品| 财经| 亚洲各类熟女们中文字幕| 无码国内精品久久人妻蜜桃| 亚洲国产超清无码专区| 国产成人一区二区三区视频免费| 精品日韩亚洲AV无码| 岛国av无码免费无禁网站| av色蜜桃一区二区三区| 国产AV国片精品有毛| 久久无码av中文出轨人妻| 亚洲欧洲国产综合一区二区| 肉大捧一进一出免费视频| 久久天天躁狠狠躁夜夜2020老熟妇| 一本久道久久综合狠狠躁av| 久久99国产精一区二区三区!| 少妇被日自拍黄色三级网络| 亚洲色成人网站www永久四虎| 东京热大乱系列无码| 胶州市| 精品九九人人做人人爱| 国产精品白浆免费视频| 无码成人一区二区三区| 国产精品99久久免费| 亚洲av成人午夜福利| 高清国产精品人妻一区二区| 亚洲av色在线观看国产| 色婷婷日日躁夜夜躁| 亚洲成人免费一级av| 亚洲欧美综合中文| 国产资源精品中文字幕| 无人去码一码二码三码区| 国产精品无码久久久久| 蜜臀午夜一区二区在线播放| 国产一区日韩二区欧美三区| 少妇粗大进出白浆嘿嘿视频| 亚洲中文无码av永久不收费| 伊人成人在线视频免费| 亚洲全乱码精品一区二区| 精品一区二区不卡免费|