6

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

 1 year ago
source link: https://www.cnblogs.com/InCerry/p/net-performance-series-2-newtonsoft-vs-system-text-json-2bf43e037db0.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

微软终于追上了?

1666864481-339193792955af68a7bbabf7cc734934.jpg

图片来自 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

1666864481-608ec13ae2f360670c97e8c5bcaf12a4.png

尽管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

1666864481-4cf966572594f19af76e29d2fbcf8c69.png

我们可以看到对于许多小对象来说,性能又快了近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

1666864481-27681798a186aead95c736cccff4a95e.png

就性能而言,微软仍然远远领先于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

1666864481-ecff676fec2646071488a6f9b44ec699.png

在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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK