.NET性能系列文章二:Newtonsoft.Json vs. System.Text.Json

微軟終于追上了?

.NET性能系列文章二:Newtonsoft.Json vs. System.Text.Json

文章插圖
圖片來自 Glenn Carstens-PetersUnsplash
歡迎來到.NET性能系列的另一章 。這個系列的特點是對.NET世界中許多不同的主題進行研究、基準和比較 。正如標題所說的那樣,重點在于使用最新的.NET7的性能 。你將看到哪種方法是實現特定主題的最快方法,以及大量的技巧和竅門 , 你如何能以較低的努力最大化你的代碼性能 。如果你對這些主題感興趣 , 請繼續關注
在這篇文章中,我們將比較兩個最突出的.NET的json框架 。:Newtonsofts Json.NET 和 Microsofts System.Text.Json.
Newtonsoft.Json是NuGet上下載量最大的軟件包 , 下載量超過23億 。System.Text.Json稍稍落后 , 大約有6億次下載 。然而,我們需要考慮的是,System.Text.Json自.NET Core 3.1起就默認隨.NET SDK交付 。既然如此,Newtonsoft似乎仍然是最受歡迎的json框架 。讓我們來看看,它是否能保持它的名次,或者微軟是否在性能方面緩慢但肯定地領先 。
測試方案為了模擬現實生活中應用的真實場景 , 我們將測試兩個主要用例 。
  • 第一 , 單個大數據集的序列化和反序列化 。
  • 第二是許多小數據集的序列化和反序列化 。
一個真實的場景也需要真實的數據 。對于測試數據集,我決定使用NuGet包Bogus 。通過Bogus,我能夠快速生成許多不同的用戶,包括個人姓名、電子郵件、ID等 。
[Params(10000)]public int Count { get; set; }private List<User> testUsers;[GlobalSetup]public void GlobalSetup(){var faker = new Faker<User>().CustomInstantiator(f => new User(Guid.NewGuid(),f.Name.FirstName(),f.Name.LastName(),f.Name.FullName(),f.Internet.UserName(f.Name.FirstName(), f.Name.LastName()),f.Internet.Email(f.Name.FirstName(), f.Name.LastName())));testUsers = faker.Generate(Count);}對于基準,我們將使用每個軟件包的最新版本,目前是(2022年10月):
  • Newtonsoft.Json — 13.0.1 and
  • System.Text.Json — 7.0.0-rc.2
序列化測試序列化大對象為了測試一個大對象的序列化,我們簡單地使用List<User>,我們在GlobalSetup()方法中設置了它 。我們的基準方法看起來像這樣:
[Benchmark(Baseline = true)]public void NewtonsoftSerializeBigData() =>_ = Newtonsoft.Json.JsonConvert.SerializeObject(testUsers);[Benchmark]public void MicrosoftSerializeBigData() =>_ = System.Text.Json.JsonSerializer.Serialize(testUsers);這些方法都使用默認的ContractResolver,它只被實例化一次,因此是兩個框架中性能最好的序列化選項 。如果你使用自定義的JsonSerializerSettings,注意不要多次實例化ContractResolver,否則你會降低很多性能 。
現在我們來看看結果:
MethodCountMeanRatioAllocatedAlloc RatioNewtonsoftSerializeBigData100007.609 ms1.008.09 MB1.00MicrosoftSerializeBigData100003.712 ms0.493.42 MB0.42
.NET性能系列文章二:Newtonsoft.Json vs. System.Text.Json

文章插圖
盡管Newtonsoft在他們的第一個文檔網站上說 。
高性能:比.NET的內置JSON序列化器快
我們可以清楚地看到,到目前為止,他們并不比內置的JSON序列化器快 。至少在這個用例中是這樣 。讓我們來看看,在其他使用情況下是否也是如此 。
序列化許多小對象這個用例在實際應用中比較常見 , 例如在REST-Apis中,每個網絡請求都必須處理JSON序列化數據,并且也要用JSON序列化數據進行響應 。
為了實現這個用例,我們使用之前建立的List<User>,并簡單地循環通過它,同時單獨序列化每個用戶 。
[Benchmark(Baseline = true)]public void NewtonsoftSerializeMuchData(){foreach (var user in testUsers){_ = Newtonsoft.Json.JsonConvert.SerializeObject(user);}}[Benchmark]public void MicrosoftSerializeMuchData(){foreach (var user in testUsers){_ = System.Text.Json.JsonSerializer.Serialize(user);}}在我的機器上,這個基準測試導致了以下結果:
MethodCountMeanRatioAllocatedAlloc RatioNewtonsoftSerializeMuchData100008.087 ms1.0017.14 MB1.00MicrosoftSerializeMuchData100003.944 ms0.493.64 MB0.21
.NET性能系列文章二:Newtonsoft.Json vs. System.Text.Json

文章插圖
我們可以看到對于許多小對象來說,性能又快了近100% 。不僅System.Text.Json的性能比Newtonsoft快了一倍,而且堆分配的內存甚至少了5倍! 正如我在以前的文章中提到的 , 節省堆內存甚至比速度更重要 , 你在這里看到了 。堆內存最終將不得不被垃圾回收,這將阻塞你的整個應用程序的執行 。

推薦閱讀