效能超四倍的高效能.NET二進位制序列化庫

xfrog發表於2021-01-11

二進位制序列化在.NET中有很多使用場景,如我們使用分散式快取時,通常將快取物件序列化為二進位制資料進行快取,在ASP.NET中,很多中介軟體(如認證等)也都是用了二進位制序列化。

在.NET中我們通常使用System.Runtime.Serialization.Formatters庫中的BinaryFormatter來進行二進位制序列化,但此庫存在以下缺點:

  • 儘管.net core對BinaryFormatter進行了一些列優化,但其效能還是較低
  • 序列化結果尺寸過大,BinaryFormatter保留了非常詳細的型別後設資料。
  • 安全問題,BinaryFormatter 因為其強大的功能和易用性而廣泛用於整個 .NET 生態系統。 但是,其強大的功能也讓攻擊者能夠影響目標應用內的控制流。 成功的攻擊可能導致攻擊者能夠在目標程式的上下文中執行程式碼。(可參考此文件
  • 通過AssemblyLoadContext動態載入程式集可能無法反序列化的問題(比如使用[PluginFactory]外掛框架),例如,你在公共庫A中封裝了序列化輔助方法,在外掛程式集B中宣告瞭序列化型別,並通過公共庫A中的輔助方法進行序列化或反序列化,最後主程式集C通過獨立的AssemblyLoadContext動態載入外掛程式集B,此種場景中,B中反序列化時將會引發無法找到程式集的異常。
  • 序列化類,必須通過SerializableAttribute特性進行標註

為了解決這些缺陷,我們開源了一款獨立的高效能.NET二進位制序列化庫Xfrogcn.BinaryFormatter([Github]  [Gitee]),該庫參考了System.Text.Json庫,通過Span與Emit大大提升了序列化效能。此庫目標為.NET Standard 2.1。

Xfrogcn.BinaryFormatter具有以下優點:

  • 高效能,通過Span與Emit大大提升了效能,其效能超過System.Runtime.Serialization.Formatters庫的近四倍
  • 更小的序列化尺寸(75%)
  • 簡單易用,與System.Text.Json基本一致的API介面。
  • 反序列化時例項引用的維持
  • 型別後設資料保留,無需在反序列化時指定目標型別
  • 支援反序列化到不同的型別
  • 更安全
  • 支援AssemblyLoadContext動態載入程式集中型別的序列化
  • 無需SerializableAttribute特性標註
  • 完善的內建型別支援([支援的型別])

一、效能

與.NET內建的System.Runtime.Serialization.Formatters.Binary.BinaryFormatter二進位制序列化對比,效能最高可達到它的4倍以上,而序列化結果的大小僅只有它的75%。

以下為通過test/BinaryFormatter.Benchmark效能測試專案獲取的效能資料,其中:

  • Json指System.Text.Json,可以看到其效能的確強悍
  • XfrogcnBinary指本庫
  • SystemBinaryFormatter指.NET內建二進位制序列化庫(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter)
  • 類別Stream為採用流化方式序列化
  • 類別Bytes為直接序列化為Byte陣列或從Byte陣列反序列化 所有的測試都基於預設配置,(流化方式下預設的緩衝區大小將會明顯影響序列化效能)

序列化

序列化

Method Categories Mean
Json Stream 61.41 μs
XfrogcnBinary Stream 92.97 μs
SystemBinaryFormatter Stream 291.37 μs
Json_Bytes Bytes 59.79 μs
XfrogcnBinary_Bytes Bytes 88.67 μs

 

反序列化

反序列化

Method Categories Mean
Json Stream 100.12 μs
XfrogcnBinary Stream 96.34 μs
SystemBinaryFormatter Stream 334.68 μs
Json_Bytes Bytes 80.13 μs
XfrogcnBinary_Bytes Bytes 92.14 μs

 

二、如何使用

Xfrogcn.BinaryFormatter庫的使用非常簡單,基本與System.Text.Json一致:

序列化

序列化到流:

MemoryStream ms = new MemoryStream();
await Xfrogcn.BinaryFormatter.BinarySerializer.SerializeAsync(ms, data);

序列化到byte陣列:

var data = Xfrogcn.BinaryFormatter.BinarySerializer.Serialize(data);

反序列化

從流中反序列化:

var obj = await Xfrogcn.BinaryFormatter.BinarySerializer.DeserializeAsync(stream);

從byte陣列反序列化:

var obj = Xfrogcn.BinaryFormatter.BinarySerializer.Deserialize(data);

反序列化為指定型別:

var obj = await Xfrogcn.BinaryFormatter.BinarySerializer.DeserializeAsync<T>(stream);
或者:
var obj = Xfrogcn.BinaryFormatter.BinarySerializer.Deserialize<T>(data);

當然,你也可以在序列化與反序列化時指定不同的配置(),更詳細的使用說明請參考[快速開始]

三、注意事項

  • 與System.Text.Json的設計一致,由於型別解析、序列化轉換器等快取都是以配置例項為基礎,即每一個配置例項的快取是獨立的,故請使用共享的配置例項,請勿為每一次序列化分配新的配置例項
  • 在流模式下,預設緩衝區的大小會極大地影響讀取效能,請根據實際情況進行詳細的測試以獲取合適的緩衝區設定(預設設定可適合大多數場景)

 

開源需要大家的努力,有興趣的同學,歡迎提交程式碼,一起完善!

相關文章