[.NET 開源] 高性能的 Swifter.MessagePack 已發布,並附帶新版本的 Swifter.Json 和 Swifter.Data。
抱歉各位朋友,由於各種私事公事,本應該在 19 年底發布的 Swifter.MessagePack 庫延遲了這麼久才發布,我深感抱歉。
MsgPack 簡介
MsgPack 一種非常輕巧的二進位數據交換格式,巧妙的設計讓它相比其他二進位數據格式更可讀,並且有著不錯的壓縮率和邏輯性能,是目前相當火熱的數據交換格式。
Swifter.MessagePack 遵循 MsgPack 新的規範實現;相比 .NET 其他 MsgPack 序列化庫,Swifter.MessagePack 有著更好的性能,生成的內容更緊湊合理且更簡單易用。
Nuget:Swifter.MessagePack,Swifter.Json,Swifter.Data
GitHub:Swifter.MessagePack,Swifter.Json
如果您想使用 Swifter 庫,請在 Nuget 上安裝/下載最新版本,如需單文件版本,請自行生成/合併。
簡單使用 Swifter.MessagePack
MessagePackFormatter 類內部還有數十個方法重載,包括靜態和實例方法,總有一些適合您;這些方法都是執行緒安全的。
更多使用方法請參考早期關於 Swifter.Json 的文章,GitHub 或 Wiki;學習交流進 Swifter 的 QQ 群:133630914(新群,歡迎加入)。
Swifter 框架的特性
(1) Swifter 可以運行在 .NET Framework 2.0+, .NET Core 2.0+, .NET Standard 2.0+, MONO JIT, MONO AOT, Xamarin.Android, Xamarin.iOS, Unity JIT 等平台/運行時上,Unity IL2CPP 運行時由於沒有我們測試環境,不知可否正常運行,更多資訊請看下面的 AOT 說明。
(2) Swifter 有著深層的抽象封裝,這雖然帶來了一些性能和記憶體的損耗,但也獲得了更高的擴展性;Swifter.Json/Swifter.MessagePack/Swifter.Data 的可公用的程式碼非常多,這使得在 Swifter 上實現一個新的序列化庫只需要編寫少量程式碼即可實現,這是其他框架難實現的。
(3) 雖然 Swifter 有很多介面和抽象編程,但是 Swifter 並沒有因此比其他的框架慢或記憶體佔用大,反比它們更快和更小記憶體佔用;這是因為 Swifter 從來都是使用更好演算法和邏輯來獲取性能,而不是使用更直接的程式碼獲取直接的性能。
(4) 作為類庫開發者,我們深知每個人開發和測試的側重點都與他人不一樣,自己找出自己的問題太難,所以 Swifter.Json 和 Swifter.MessagePack 除了我們自己的測試單元之外, 還 “偷” 了 Newtonsoft, Neuecc 和 Spanjson 的 5000+ 個測試單元( 去除了 Newtonsoft 的部分測試單元);現已測試通過 4200+ 個,不通過 800+ 個是我們認為可以允許或是更加合理的行為。(不勞而獲的測試單元確實用著很爽,但事實是我們”搬”這些測試單元用了 3 天😫,無腦替換改到手指抽筋)
Swifter.Json 和 Swifter.MessagePack
(1) Swifter.MessagePack 和 Swifter.Json 一樣,都有著非常優異的性能和極小的額外記憶體分配。
(2) Swifter.MessagePack 和 Swifter.Json 的 API 大致相同,如果使用者同時使用它們,那麼可以極小成本在它們之間切換。
(3) 得益於 Swifter.Core 的強大數據映射,Swifter.MessagePack 和 Swifter.Json 都同時支援 .NET 上大多數常用的數據結構和類型。
(4) Swifter.MessagePack 和 Swifter.Json 對重複引用的對象的表示方式不一樣,在開啟 MultiReferencingReference 配置項後,Swifter.Json 將使用 { “$ref”: “#/obj/1/target” } 來表示重複引用的對象,而 Swifter.MessagePack 使用對象在 MsgPack 內容的偏移量表示重複引用的對象;相比之下 Swifter.MessagePack 的方案更簡單性能更快,但是可讀性較差,不過說來 MsgPack 本來就是要專門的工具才能閱讀😄。
(5) Swifter.MessagePack 在序列化基礎類型時,在保證精度不丟失的前提下,將大數據類型轉換為更小數據類型,以得到更緊湊的 MsgPack 內容(如將 double 123 轉換為 int 123,int 123 只需要 1 個位元組即可表示,如果不做轉換則需要 9 個位元組表示)。
(6) Swifter.MessagePack 在序列化未知長度的集合時(如 Enumerable<T>),會將長度定義為四位元組 (FixArray32),然後在寫入完成後把實際長度賦予這四位元組長度;這樣雖然在較短的未知長度集合時,將產生 1-3 個 0;但是這避免了將未知長度的集合轉換為 List<T> 或 T[], 這提高了性能也減少了記憶體分配,這是不虧的(因為未知長度的集合很常用,如 Linq,DbDataReader 等)。
新版本做了啥?
(1) 主要是解決了已知 BUG,包括了 Issues 上提到的幾個。
(2) 允許將 “” 值解析為 DateTime, int?, double 等基礎類型的默認值,但是需要啟用 EmptyStringAsDefault 配置項,默認未開啟。
(3) 解決了 Swifter.Json 浮點數: float, double 失真的問題,並增加了 UseSystemFloatingPointsMethods 配置項使用系統的浮點數方法,此配置項的更多說明請看該配置項的注釋。
(4) 增加了序列化的事件:ObjectFiltering 和 ArrayFiltering,這兩個事件可以對正在序列化中的 鍵/值 做處理和篩選,包括駝峰命名法,忽略一些值等。它們被放在 JsonFormatter 和 MessagePackFormatter 的實例裡面。
(5) 增加了 .NET 對象的持久序列化和反序列化功能,這個功能將對象序列化為包含類型資訊和欄位值的內容,不包含邏輯資訊;使用 SerializationBox<T> 盒子使用此功能。圖示:
更多新增的功能請繼續看以下內容。
AOT
在 Swifter 新版本里,AOT 的 JIT 的界限更加明顯,由 VersionDifferences.IsSupportEmit 欄位標識;當這個欄位為 true 表示當前平台是 JIT 運行時,Swifter 將在一些類中使用 Emit 技術提高性能;當此欄位為 false 時,Swifter 會完全不使用 Emit 技術。
因我們設備有限,無法提供大規模的平台測試,但我們非常希望可以 Swifter 可以支援更多的平台,所以希望朋友們加 Swifter 交流 QQ 群:(133630914),在這裡我們可以更快的提供回饋。
直接文檔讀取/寫入的 API
通常情況下,將小型對象序列化為 Json/MsgPack 和將小型 Json/MsgPack 反序列化為對象是 .NET 程式中常見的操作,Swifter 也正以此為常用場景做優化,所以 Swifter 在對小型數據操作時性能最佳,且相比其他 Json/MsgPack 解析庫優勢明顯。
但在大型數據下優勢減少,這主要原因是大型數據的存儲需要實體類或字典/集合存儲,創建/填充/遍歷這些對象消耗了大量資源(介面編程的損耗);所以 Swifter 提供了直接讀取/寫入的 API 來繞開了對存儲介質的操作,以更快更小損耗的讀寫大型數據。
使用 JsonFormatter.CreateJsonReader/MessagePackFormatter.CreateMessagePackReader 函數來創建文檔讀取器,使用 JsonFormatter.CreateJsonWriter/MessagePackFormatter.CreateMessagePackWriter 函數來創建文檔寫入器。
使用文檔讀取器完整的讀取一個 Json/MsgPack 文檔將比反序列化為對象快 4-8 倍!使用文檔寫入器生成文檔的性能與將實體類序列化為 Json/MsgPack 相差較小,前提是您已構建好了這些對象😄。
讀取器演示:
寫入器演示:
擁有簡單預測數組的長度的能力
Swifter 在對小型數組,部分集合寫入時,會根據數組的類型,來源(Data,Json,MsgPack 等),名稱等資訊並結合之前的一些長度記錄,簡單的預測出新的數組的長度;在寫入完成後,如果預測長度與實際長度不符,則擴展或壓縮為實際長度;如果與實際長度相符,則不需要重新創建新數組。此能力有效提高反序列化小型數組和部分集合性能,並且減少額外記憶體分配。
在其他高性能的 Json 解析庫,它們使用 ArrayPool<T> 同樣可以提高性能和減少記憶體分配;但是由於 Swifter 對兼容性的要求,使得我們不能使用 ArrayPool<T> 方案;在數組的長度比較穩定的情況下,我們的方案更好;但在數組長度非常不穩定的情況下,我們的方案可能仍需要 1-3 次的擴容/壓縮。
假定有序的對象反序列化
Swifter.Json 和 Swifter.MessagePack 都支援了假定有序的對象反序列化,當一個 Json/MsgPack 的對象與當前的實體類對象的欄位順序一致時,將有效提升反序列化性能。
此操作默認不開啟,可以使用 AsOrderedObjectDeserialize 配置項開啟。
高性能的反射封裝
Swifter.Core 里提供了一些對反射封裝的類,它們放在 Swifter.Reflection 命名空間下;這些類型主要功能就是提高了系統反射的性能;XObjectRW 正是使用它們實現不依賴 Emit 的高性能對象讀寫器。
雖然放棄一些安全性檢查可以提高更多的性能,但是我們並沒有這麼做;我們仍然有類型安全檢查和防溢出檢查(事實上讀寫欄位和屬性大多數的損害都在這裡,如果去掉這些檢查將得到上百倍的性能;事實上這些檢查只起到了提示程式設計師不能這麼做的作用,程式實際運行時這些檢查無意義)。
高效的數字 ToString 和 Parse 方法
Swifter.Core 提供了一些高性能數字演算法,包括 Int64, UInt64, Double, Single, Decimal 的 Parse 和 ToString 演算法,它們被放在 Swifter.Tools.NumberHelper 里,這些演算法被應用與 Swifter.Json 和一些其他地方,這些演算法支援 2-64 進位。
XConvert 萬能類型轉換器
Swifter.Tools.XConvert.Convert<TSource, TDestination> 是一個功能強大的萬能類型轉換函數,它在初始化時嘗試以下方式獲取合適的轉換函數:
(1) 包含在 System.Convert 里的基礎轉換函數;
(2) 類型兼容的隱式轉換(如:從子類轉換為父類,從 Int32 轉換為 Int64,從 Int64 轉換為 Double)。
(3) 原類型和目標類型中的 static implicit operator (隱式轉換) 函數。
(4) 原類型中的 ToXXX 實例函數。
(5) 目標類型中的 Parse 和 ValueOf 靜態函數。
(7) 目標類型的構造函數。
(8) 原類型和目標類型中的 static explicit operator (顯式轉換) 函數。
(9) 當以上方法都沒有找到合適函數時,將使用 (TDestination)(object)value 進行強制轉換。
簡單示例:
性能測試
ServiceStack.Json, Jil, LitJson, NetJson 等庫因為出錯太多未展示出來;如果有需要,您可以到 GitHub 上自行克隆/修改/運行,已收錄了 .NET 的大多數 Json 序列化庫。
更多實用功能等你發現…
Swifter.Core 還提供了許許多多的工具類,包括反射,委託,類型轉換,字元串,加密,哈希,數字,日期,數組和集合等工具,它們被放在 Swifter.Tools 命名空間下,您可以使用它們來提高開發效率和運行效率。
Swifter.RW 命名空間是整個 Swifter 框架的核心,它主要邏輯是:從讀取器中讀取值,寫入到寫入器中;如:從 JsonReader 讀取值到 ObjectWriter 或 DictionaryWriter 中;熟悉它們就等於精通了 Swifter 框架。
Swifter.Json/Swifter.MessagePack 有一個非常重要的配置項 JsonFormatterOptions/MessagePackFormatterOptions;使用前建議先閱讀它們,以配置更適合您系統的序列化和反序列化方案。
…
最後附上 Swifter.Data 的簡介
Swifter.Data 是一個小型的 ORM 工具,它相比 Dapper 性能要快一些,功能要強大一些。
感謝閱讀