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

文章,性能,Json,NET · 浏览次数 : 3260

小编点评

**MethodCountMeanRatioAllocatedAlloc Ratio** * 使用System.Text.Json的**JsonSerializer**对**serializedTestUsersList**进行反序列化。 * 效率高,** MethodCount**为**1000015**。 ****Benchmark** * **MicrosoftDeserializeBigData**:**100007** * **NewtonsoftDeserializeBigData**:**1000021** * **MicrosoftDeserializeMuchData**:**1000015** * **NewtonsoftDeserializeMuchData**:**1000015** ****结果** * 使用**System.Text.Json**的**JsonSerializer**对**serializedTestUsersList**进行反序列化比**Newtonsoft**的**JsonSerializer**更**高效**。 * **MicrosoftDeserializeBigData**和**MicrosoftDeserializeMuchData**的效率分别比**Newtonsoft**的效率高**5倍以上。 * **Newtonsoft**的效率比**System.Text.Json**略低,但仍**快**于**微软**框架。 ****结论** * 使用**System.Text.Json**比**Newtonsoft**的**JsonSerializer**更高效,**性能**也更**快**。 * **微软**的**System.Text.Json**框架在所有测试用例中至少快了一倍,命名为**.NET 7**。

正文

微软终于追上了?

图片来自 Glenn Carstens-Peters Unsplash

欢迎来到.NET性能系列的另一章。这个系列的特点是对.NET世界中许多不同的主题进行研究、基准和比较。正如标题所说的那样,重点在于使用最新的.NET7的性能。你将看到哪种方法是实现特定主题的最快方法,以及大量的技巧和窍门,你如何能以较低的努力最大化你的代码性能。如果你对这些主题感兴趣,请继续关注

在这篇文章中,我们将比较两个最突出的.NET的json框架。:
Newtonsofts Json.NETMicrosofts 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,否则你会降低很多性能。

现在我们来看看结果:

Method Count Mean Ratio Allocated Alloc Ratio
NewtonsoftSerializeBigData 10000 7.609 ms 1.00 8.09 MB 1.00
MicrosoftSerializeBigData 10000 3.712 ms 0.49 3.42 MB 0.42

尽管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);
    }
}

在我的机器上,这个基准测试导致了以下结果:

Method Count Mean Ratio Allocated Alloc Ratio
NewtonsoftSerializeMuchData 10000 8.087 ms 1.00 17.14 MB 1.00
MicrosoftSerializeMuchData 10000 3.944 ms 0.49 3.64 MB 0.21

我们可以看到对于许多小对象来说,性能又快了近100%。不仅System.Text.Json的性能比Newtonsoft快了一倍,而且堆分配的内存甚至少了5倍! 正如我在以前的文章中提到的,节省堆内存甚至比速度更重要,你在这里看到了。堆内存最终将不得不被垃圾回收,这将阻塞你的整个应用程序的执行。

反序列化测试

在现实世界的应用中,你不仅要序列化,还要从JSON序列化的字符串中反序列化对象。在下面的基准中,我们将再次使用Bogus,创建一组用户,但这次我们要把它们序列化为一个大的字符串,用于大数据对象,并把许多小数据对象序列化为List<string>

private string serializedTestUsers;

private List<string> serializedTestUsersList = new();

[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())
        ));

    var testUsers = faker.Generate(Count);

    serializedTestUsers = JsonSerializer.Serialize(testUsers);

    foreach (var user in testUsers.Select(u => JsonSerializer.Serialize(u)))
    {
        serializedTestUsersList.Add(user);
    }
}

反序列化大对象

第一个反序列化基准将一个大的JSON字符串反序列化为相应的.NET对象。在这种情况下,它又是List<User>,我们在前面的例子中也使用了它。

[Benchmark(Baseline = true)]
public void NewtonsoftDeserializeBigData() => 
    _ = Newtonsoft.Json.JsonConvert.DeserializeObject<List<User>>(serializedTestUsers);

[Benchmark]
public void MicrosoftDeserializeBigData() => 
    _ = System.Text.Json.JsonSerializer.Deserialize<List<User>>(serializedTestUsers);

在我的机器上运行这些基准测试,得出以下结果:

Method Count Mean Ratio Allocated Alloc Ratio
NewtonsoftDeserializeBigData 10000 21.20 ms 1.00 10.55 MB 1.00
MicrosoftDeserializeBigData 10000 12.12 ms 0.57 6.17 MB 0.59

就性能而言,微软仍然远远领先于Newtonsoft。然而,我们可以看到,Newtonsoft并没有慢一半,而是慢了40%左右,这在与序列化基准的直接比较中是一个进步。

反序列化许多小对象

本章的最后一个基准是许多小对象的反序列化。在这里,我们使用我们在上面的GlobalSetup()方法中初始化的List<string>,在一个循环中反序列化数据对象:

[Benchmark(Baseline = true)]
public void NewtonsoftDeserializeMuchData()
{
    foreach (var user in serializedTestUsersList)
    {
        _ = Newtonsoft.Json.JsonConvert.DeserializeObject<User>(user);
    }
}

[Benchmark]
public void MicrosoftDeserializeMuchData()
{
    foreach (var user in serializedTestUsersList)
    {
        _ = System.Text.Json.JsonSerializer.Deserialize<User>(user);
    }
}

其结果甚至比相关的序列化基准更令人吃惊:

Method Count Mean Ratio Allocated Alloc Ratio
NewtonsoftDeserializeMuchData 10000 15.577 ms 1.00 35.54 MB 1.00
MicrosoftDeserializeMuchData 10000 7.916 ms 0.51 4.8 MB 0.14

在Microsofts框架下,速度又快了一倍,内存效率是惊人的7倍,比Newtonsoft还要好!

总结

尽管Newtonsoft在他们的文档上说:

高性能:比.NET的内置JSON序列化器更快

很明显,至少从.NET 7开始,Microsofts的System.Text.Json在所有测试的用例中至少快了一倍,命名为。

  • 序列化一个大数据集
  • 序列化许多小数据集
  • 对一个大数据集进行反序列化
  • 对许多小数据集进行反序列化

所有这些都是在每个框架的默认序列化器设置下进行的。

不仅速度快了100%,而且在某些情况下,分配的内存甚至比Newtonsoft的效率高5倍以上。

我甚至认为,可以推断出结果,目前使用System.Text.JsonNewtonsoft.Json更快。

请记住,这些结果只对最新的.NET 7有效。如果你使用的是其他版本的.NET,情况可能正好相反,Newtonsoft可能会更快。

我希望,我的文章可以帮助你对序列化器做出选择选择,并让你对性能和基准测试的世界有一个有趣的切入点。

如果你喜欢这个系列的文章,请一定要关注我,因为还有很多有趣的话题等着你。

谢谢你的阅读!

版权

原文版权:Tobias Streng
翻译版权:InCerry
原文链接:https://medium.com/@tobias.streng/net-performance-series-2-newtonsoft-vs-system-text-json-2bf43e037db0

与.NET性能系列文章二:Newtonsoft.Json vs. System.Text.Json相似的内容:

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

微软终于追上了? 图片来自 Glenn Carstens-Peters Unsplash 欢迎来到.NET性能系列的另一章。这个系列的特点是对.NET世界中许多不同的主题进行研究、基准和比较。正如标题所说的那样,重点在于使用最新的.NET7的性能。你将看到哪种方法是实现特定主题的最快方法,以及大量的

.NET性能系列文章一:.NET7的性能改进

这些方法在.NET7中变得更快 照片来自 CHUTTERSNAP 的 Unsplash 欢迎阅读.NET性能系列的第一章。这一系列的特点是对.NET世界中许多不同的主题进行研究、比较性能。正如标题所说的那样,本章节在于.NET7中的性能改进。你将看到哪种方法是实现特定功能最快的方法,以及大量的技巧和

是什么让.NET7的Min和Max方法性能暴增了45倍?

简介 在之前的一篇文章.NET性能系列文章一:.NET7的性能改进中我们聊到Linq中的Min()和Max()方法.NET7比.NET6有高达45倍的性能提升,当时Benchmark代码和结果如下所示: [Params(1000)] public int Length { get; set; } p

使用C#编写.NET分析器(三)

## 译者注 这是在Datadog公司任职的Kevin Gosse大佬使用C#编写.NET分析器的系列文章之一,在国内只有很少很少的人了解和研究.NET分析器,它常被用于APM(应用性能诊断)、IDE、诊断工具中,比如Datadog的APM,Visual Studio的分析器以及Rider和Resh

使用C#编写.NET分析器(完结)

## 译者注 这是在Datadog公司任职的Kevin Gosse大佬使用C#编写.NET分析器的系列文章之一,在国内只有很少很少的人了解和研究.NET分析器,它常被用于APM(应用性能诊断)、IDE、诊断工具中,比如Datadog的APM,Visual Studio的分析器以及Rider和Resh

使用C#编写.NET分析器-第二部分

## 译者注 这是在Datadog公司任职的Kevin Gosse大佬使用C#编写.NET分析器的系列文章之一,在国内只有很少很少的人了解和研究.NET分析器,它常被用于APM(应用性能诊断)、IDE、诊断工具中,比如Datadog的APM,Visual Studio的分析器以及Rider和Resh

[转帖]FIO – IO压力测试工具

`https://blog.csdn.net/younger_china/article/details/71129541` 《存储工具系列文章》主要介绍存储相关的测试和调试工具,包括不限于dd、fio、vdbench、iozone、iometer、cosbench等性能负载工具,及strace等调

.NET Emit 入门教程:第七部分:实战项目1:将 DbDataReader 转实体

经过前面几个部分学习,相信学过的同学已经能够掌握 .NET Emit 这种中间语言,并能使得它来编写一些应用,以提高程序的性能。随着 IL 指令篇的结束,本系列也已经接近尾声,在这接近结束的最后,会提供几个可供直接使用的示例,以供大伙分析或使用在项目中。

.Net性能测试工具BenchmarkDotNet学习

.Net性能测试工具BenchmarkDotNet学习 BenchmarkDotNet 是一个用于性能基准测试的开源框架。它可以让开发人员编写简单易懂的代码,并测量和分析这些代码的性能表现,从而帮助开发人员优化其代码,以达到更高的性能和更好的效率。 源码地址:https://github.com/d

.NET性能优化-复用StringBuilder

在之前的文章中,我们介绍了dotnet在字符串拼接时可以使用的一些性能优化技巧。比如: 为StringBuilder设置Buffer初始大小 使用ValueStringBuilder等等 不过这些都多多少少有一些局限性,比如StringBuilder还是会存在new StringBuilder()这