微软终于追上了? 图片来自GlennCarstensPeters〔1〕Unsplash〔2〕 欢迎来到。NET性能系列的另一章。这个系列的特点是对。NET世界中许多不同的主题进行研究、基准和比较。正如标题所说的那样,重点在于使用最新的。NET7的性能。你将看到哪种方法是实现特定主题的最快方法,以及大量的技巧和窍门,你如何能以较低的努力最大化你的代码性能。如果你对这些主题感兴趣,请继续关注 在这篇文章中,我们将比较两个最突出的。NET的json框架。:NewtonsoftsJson。NET〔3〕和MicrosoftsSystem。Text。Json〔4〕。 Newtonsoft。Json是NuGet上下载量最大的软件包,下载量超过23亿。System。Text。Json稍稍落后,大约有6亿次下载。然而,我们需要考虑的是,System。Text。Json自。NETCore3。1起就默认随。NETSDK交付。既然如此,Newtonsoft似乎仍然是最受欢迎的json框架。让我们来看看,它是否能保持它的名次,或者微软是否在性能方面缓慢但肯定地领先。 测试方案 为了模拟现实生活中应用的真实场景,我们将测试两个主要用例。 第一,单个大数据集的序列化和反序列化。 第二是许多小数据集的序列化和反序列化。 一个真实的场景也需要真实的数据。对于测试数据集,我决定使用NuGet包Bogus〔5〕。通过Bogus,我能够快速生成许多不同的用户,包括个人姓名、电子邮件、ID等。〔Params(10000)〕 publicintCount{get;set;} privateListUsertestUsers; 〔GlobalSetup〕 publicvoidGlobalSetupstrongtoutiaooriginspanclasshighlighttextstrong { varfakernewFakerUser 。CustomInstantiator(fnewUser( 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) )); testUsersfaker。Generate(Count); } 对于基准,我们将使用每个软件包的最新版本,目前是(2022年10月): Newtonsoft。Json13。0。1and System。Text。Json7。0。0rc。2〔6〕 序列化测试序列化大对象 为了测试一个大对象的序列化,我们简单地使用ListUser,我们在GlobalSetup方法中设置了它。我们的基准方法看起来像这样:〔Benchmark(Baselinetrue)〕 publicvoidNewtonsoftSerializeBigData Newtonsoft。Json。JsonConvert。SerializeObject(testUsers); 〔Benchmark〕 publicvoidMicrosoftSerializeBigData System。Text。Json。JsonSerializer。Serialize(testUsers); 这些方法都使用默认的ContractResolver,它只被实例化一次,因此是两个框架中性能最好的序列化选项。如果你使用自定义的JsonSerializerSettings,注意不要多次实例化ContractResolver,否则你会降低很多性能。 现在我们来看看结果:MethodCountMeanRatioAllocatedAllocRatioNewtonsoftSerializeBigData100007。609ms1。008。09MB1。00MicrosoftSerializeBigData100003。712ms0。493。42MB0。42 尽管Newtonsoft在他们的第一个文档网站〔7〕上说。 高性能:比。NET的内置JSON序列化器快 我们可以清楚地看到,到目前为止,他们并不比内置的JSON序列化器快。至少在这个用例中是这样。让我们来看看,在其他使用情况下是否也是如此。 序列化许多小对象 这个用例在实际应用中比较常见,例如在RESTApis中,每个网络请求都必须处理JSON序列化数据,并且也要用JSON序列化数据进行响应。 为了实现这个用例,我们使用之前建立的ListUser,并简单地循环通过它,同时单独序列化每个用户。〔Benchmark(Baselinetrue)〕 publicvoidNewtonsoftSerializeMuchDatastrongtoutiaooriginspanclasshighlighttextstrong { foreach(varuserintestUsers) { Newtonsoft。Json。JsonConvert。SerializeObject(user); } } 〔Benchmark〕 publicvoidMicrosoftSerializeMuchDatastrongtoutiaooriginspanclasshighlighttextstrong { foreach(varuserintestUsers) { System。Text。Json。JsonSerializer。Serialize(user); } } 在我的机器上,这个基准测试导致了以下结果:MethodCountMeanRatioAllocatedAllocRatioNewtonsoftSerializeMuchData100008。087ms1。0017。14MB1。00MicrosoftSerializeMuchData100003。944ms0。493。64MB0。21 我们可以看到对于许多小对象来说,性能又快了近100。不仅System。Text。Json的性能比Newtonsoft快了一倍,而且堆分配的内存甚至少了5倍!正如我在以前的文章中提到的,节省堆内存甚至比速度更重要,你在这里看到了。堆内存最终将不得不被垃圾回收,这将阻塞你的整个应用程序的执行。 反序列化测试 在现实世界的应用中,你不仅要序列化,还要从JSON序列化的字符串中反序列化对象。在下面的基准中,我们将再次使用Bogus,创建一组用户,但这次我们要把它们序列化为一个大的字符串,用于大数据对象,并把许多小数据对象序列化为Liststring。privatestringserializedTestUsers; privateListstringserializedTestUsersListnew; 〔GlobalSetup〕 publicvoidGlobalSetupstrongtoutiaooriginspanclasshighlighttextstrong { varfakernewFakerUser 。CustomInstantiator(fnewUser( 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) )); vartestUsersfaker。Generate(Count); serializedTestUsersJsonSerializer。Serialize(testUsers); foreach(varuserintestUsers。Select(uJsonSerializer。Serialize(u))) { serializedTestUsersList。Add(user); } } 反序列化大对象 第一个反序列化基准将一个大的JSON字符串反序列化为相应的。NET对象。在这种情况下,它又是ListUser,我们在前面的例子中也使用了它。〔Benchmark(Baselinetrue)〕 publicvoidNewtonsoftDeserializeBigData Newtonsoft。Json。JsonConvert。DeserializeObjectListUser(serializedTestUsers); 〔Benchmark〕 publicvoidMicrosoftDeserializeBigData System。Text。Json。JsonSerializer。DeserializeListUser(serializedTestUsers); 在我的机器上运行这些基准测试,得出以下结果:MethodCountMeanRatioAllocatedAllocRatioNewtonsoftDeserializeBigData1000021。20ms1。0010。55MB1。00MicrosoftDeserializeBigData1000012。12ms0。576。17MB0。59 就性能而言,微软仍然远远领先于Newtonsoft。然而,我们可以看到,Newtonsoft并没有慢一半,而是慢了40左右,这在与序列化基准的直接比较中是一个进步。 反序列化许多小对象 本章的最后一个基准是许多小对象的反序列化。在这里,我们使用我们在上面的GlobalSetup方法中初始化的Liststring,在一个循环中反序列化数据对象:〔Benchmark(Baselinetrue)〕 publicvoidNewtonsoftDeserializeMuchDatastrongtoutiaooriginspanclasshighlighttextstrong { foreach(varuserinserializedTestUsersList) { Newtonsoft。Json。JsonConvert。DeserializeObjectUser(user); } } 〔Benchmark〕 publicvoidMicrosoftDeserializeMuchDatastrongtoutiaooriginspanclasshighlighttextstrong { foreach(varuserinserializedTestUsersList) { System。Text。Json。JsonSerializer。DeserializeUser(user); } } 其结果甚至比相关的序列化基准更令人吃惊:MethodCountMeanRatioAllocatedAllocRatioNewtonsoftDeserializeMuchData1000015。577ms1。0035。54MB1。00MicrosoftDeserializeMuchData100007。916ms0。514。8MB0。14 在Microsofts框架下,速度又快了一倍,内存效率是惊人的7倍,比Newtonsoft还要好! 总结 尽管Newtonsoft在他们的文档〔8〕上说: 高性能:比。NET的内置JSON序列化器更快 很明显,至少从。NET7开始,Microsofts的System。Text。Json在所有测试的用例中至少快了一倍,命名为。 序列化一个大数据集 序列化许多小数据集 对一个大数据集进行反序列化 对许多小数据集进行反序列化 所有这些都是在每个框架的默认序列化器设置下进行的。 不仅速度快了100,而且在某些情况下,分配的内存甚至比Newtonsoft的效率高5倍以上。 我甚至认为,可以推断出结果,目前使用System。Text。Json比Newtonsoft。Json更快。 请记住,这些结果只对最新的。NET7有效。如果你使用的是其他版本的。NET,情况可能正好相反,Newtonsoft可能会更快。 我希望,我的文章可以帮助你对序列化器做出选择选择,并让你对性能和基准测试的世界有一个有趣的切入点。 如果你喜欢这个系列的文章,请一定要关注我,因为还有很多有趣的话题等着你。 谢谢你的阅读! 版权 原文版权:TobiasStreng翻译版权:InCerry 原文链接:https:medium。comtobias。strengnetperformanceseries2newtonsoftvssystemtextjson2bf43e037db0 参考资料 〔1〕 GlennCarstensPeters:https:unsplash。comglenncarstenspeters?utmsourcemediumutmmediumreferral 〔2〕 Unsplash:https:unsplash。com?utmsourcemediumutmmediumreferral 〔3〕 NewtonsoftsJson。NET:https:www。newtonsoft。comjson 〔4〕 MicrosoftsSystem。Text。Json:https:www。nuget。orgpackagesSystem。Text。Json 〔5〕 Bogus:https:github。combchavezBogus 〔6〕 7。0。0rc。2:https:www。nuget。orgpackagesSystem。Text。Json7。0。0rc。2。22472。3 〔7〕 第一个文档网站:https:www。newtonsoft。comjsonhelphtmlIntroduction。htm 〔8〕 文档:https:www。newtonsoft。comjsonhelphtmlIntroduction。htm