2


Benchmarking C# Serializers




Date Added (UTC):

03 May 2024 @ 14:19

Date Updated (UTC):

03 May 2024 @ 14:19


.NET Version(s):

.NET 8

Tag(s):

#(De)Serialization #Compression #Reflection


Added By:
Profile Image

Blog   
Bangkok, Thailand    
Senior Software Engineer

Benchmark Results:





Benchmark Code:



using Avro.IO;
using Avro.Reflect;
using Avro;
using BenchmarkDotNet.Attributes;
using MessagePack;
using MongoDB.Bson;
using ProtoBuf;
using System.Text.Json;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Reports;

namespace SerializerComparison;

[Config(typeof(Config))]
[HideColumns(Column.RatioSD, Column.AllocRatio)]
[MemoryDiagnoser(false)]
public class SerializerBenchmark
{
    private Order _order = new Order().Create();

    private string orderSchema = @"
        {
            ""type"": ""record"",
            ""name"": ""Order"",
            ""fields"": [
                { ""name"": ""Id"", ""type"": ""string"" },
                { ""name"": ""Name"", ""type"": ""string"" },
                { ""name"": ""Category"", ""type"": ""string"" },
                { ""name"": ""User"", ""type"": ""string"" },
                { ""name"": ""TotalAmount"", ""type"": ""long"" }
            ]
        }";

    private ReflectWriter<Order> _avroWriter;

    private class Config : ManualConfig
    {
        public Config()
        {
            SummaryStyle = SummaryStyle.Default.WithRatioStyle(RatioStyle.Trend);
        }
    }

    [GlobalSetup]
    public void Setup()
    {
        Schema schema = Schema.Parse(orderSchema);

        _avroWriter = new ReflectWriter<Order>(schema);
    }

    [Benchmark(Baseline = true)]
    public void Json()
        => Newtonsoft.Json.JsonConvert.SerializeObject(_order);

    [Benchmark]
    public void JsonText()
       => JsonSerializer.Serialize(_order);

    [Benchmark]
    public void Protobuf()
    {
        using var protobufMs = new MemoryStream();
        Serializer.Serialize(protobufMs, _order);
    }

    [Benchmark]
    public void Avro()
    {
        using var avroMs = new MemoryStream();

        _avroWriter.Write(_order, new BinaryEncoder(avroMs));
    }

    [Benchmark]
    public void MessagePack() => MessagePackSerializer.Serialize(_order);

    [Benchmark]
    public void Bson() => _order.ToBson();
}

[ProtoContract]
[MessagePackObject]
public class Order
{
    [ProtoMember(1)] // protobuf
    [Key(0)] // messagePack
    public string Id { get; set; } = Guid.NewGuid().ToString();

    [ProtoMember(2)]
    [Key(1)]
    public string Name { get; set; }

    [ProtoMember(3)]
    [Key(2)]
    public string Category { get; set; }

    [ProtoMember(4)]
    [Key(3)]
    public long TotalAmount { get; set; }

    [ProtoMember(5)]
    [Key(4)]
    public string User { get; set; }

    public Order Create()
    {
        return new Order
        {
            Name = "Book Order",
            Category = "Books",
            TotalAmount = 100,
            User = Guid.NewGuid().ToString()
        };
    }
}

// .NET 8 Lowered C# Code unavailable due to errors:
error CS0246: The type or namespace name 'Avro' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'MessagePack' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'MongoDB' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoBuf' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoContractAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoContract' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'MessagePackObjectAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'MessagePackObject' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ReflectWriter<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoMemberAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoMember' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'KeyAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Key' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Schema' could not be found (are you missing a using directive or an assembly reference?)
error CS0103: The name 'Schema' does not exist in the current context
error CS0103: The name 'Newtonsoft' does not exist in the current context
error CS0246: The type or namespace name 'MemoryStream' could not be found (are you missing a using directive or an assembly reference?)
error CS0103: The name 'Serializer' does not exist in the current context
error CS0246: The type or namespace name 'BinaryEncoder' could not be found (are you missing a using directive or an assembly reference?)
error CS0103: The name 'MessagePackSerializer' does not exist in the current context
error CS1061: 'Order' does not contain a definition for 'ToBson' and no accessible extension method 'ToBson' accepting a first argument of type 'Order' could be found (are you missing a using directive or an assembly reference?)

// .NET 8 IL Code unavailable due to errors:
error CS0246: The type or namespace name 'Avro' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'MessagePack' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'MongoDB' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoBuf' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ReflectWriter<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoContractAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoContract' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'MessagePackObjectAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'MessagePackObject' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoMemberAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoMember' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'KeyAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Key' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Schema' could not be found (are you missing a using directive or an assembly reference?)
error CS0103: The name 'Schema' does not exist in the current context
error CS0103: The name 'Newtonsoft' does not exist in the current context
error CS0246: The type or namespace name 'MemoryStream' could not be found (are you missing a using directive or an assembly reference?)
error CS0103: The name 'Serializer' does not exist in the current context
error CS0246: The type or namespace name 'BinaryEncoder' could not be found (are you missing a using directive or an assembly reference?)
error CS0103: The name 'MessagePackSerializer' does not exist in the current context
error CS1061: 'Order' does not contain a definition for 'ToBson' and no accessible extension method 'ToBson' accepting a first argument of type 'Order' could be found (are you missing a using directive or an assembly reference?)

// .NET 8 Jit Asm Code unavailable due to errors:
error CS0246: The type or namespace name 'Avro' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'MessagePack' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'MongoDB' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoBuf' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ReflectWriter<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoContractAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoContract' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'MessagePackObjectAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'MessagePackObject' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoMemberAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ProtoMember' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'KeyAttribute' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Key' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'Schema' could not be found (are you missing a using directive or an assembly reference?)
error CS0103: The name 'Schema' does not exist in the current context
error CS0103: The name 'Newtonsoft' does not exist in the current context
error CS0246: The type or namespace name 'MemoryStream' could not be found (are you missing a using directive or an assembly reference?)
error CS0103: The name 'Serializer' does not exist in the current context
error CS0246: The type or namespace name 'BinaryEncoder' could not be found (are you missing a using directive or an assembly reference?)
error CS0103: The name 'MessagePackSerializer' does not exist in the current context
error CS1061: 'Order' does not contain a definition for 'ToBson' and no accessible extension method 'ToBson' accepting a first argument of type 'Order' could be found (are you missing a using directive or an assembly reference?)


Benchmark Description:


I made a comparison between these libraries: - NewtonSoft.Json - System.Text.Json - Protobuf - Apache.Avro - MessagePack - Bson In the case of performance, it seems **MessagePack** is the best and the **Protobuf** is the second. If you want to know about output size, here is my investigation for the same model: ```bash | Library | Output size| |------------ |-----------:| | NewtonSoft | 148 B | | System.Text | 148 B | | Protobuf | 97 B | | Apache.Avro | 93 B | | MessagePack | 95 B | | Bson | 160 B | ``` For output size **Apache.Avro** is the best with lowest size. I also have a video with a detailed explanation of those serializer, ## You can watch the video here: 👇 [![Watch the video](https://img.youtube.com/vi/qWacutAW3e8/hqdefault.jpg)](https://youtu.be/qWacutAW3e8)

The provided benchmark code is designed to compare the performance and efficiency of different serialization methods in .NET. Serialization is the process of converting an object into a format that can be persisted or transported. The efficiency of serialization can significantly impact the performance of applications, especially those that rely heavily on data exchange or persistence operations. The .NET version isn't explicitly mentioned, but given the libraries and syntax used, it should be compatible with .NET Core 3.1 or .NET 5/6/7. ### General Setup - **Configuration (`Config` class):** Custom configuration for the benchmark, focusing on the display of summary results. The `SummaryStyle` is set to show trends in the ratio style, which helps in understanding the performance changes relative to the baseline. - **Global Setup (`Setup` method):** Initializes shared resources before running the benchmarks. It parses the Avro schema and sets up the Avro writer for the `Order` class. - **Order Class:** Represents the data model to be serialized. It is decorated with attributes for Protobuf and MessagePack to define serialization behavior. ### Benchmarks Each benchmark method tests the serialization performance of a different library or approach. Here's an overview of each method and what it aims to measure: 1. **Json (Newtonsoft.Json):** - **Purpose:** Measures the performance of serializing the `Order` object to JSON using the Newtonsoft.Json library. - **Importance:** Newtonsoft.Json has been a widely used JSON serialization library in the .NET ecosystem, known for its flexibility and feature set. - **Expected Insights:** Provides a baseline for JSON serialization performance. It's often used as a comparison point for other serialization methods due to its popularity. 2. **JsonText (System.Text.Json):** - **Purpose:** Measures the performance of serializing the `Order` object to JSON using the System.Text.Json library. - **Importance:** System.Text.Json is part of the .NET Core framework, optimized for performance and lower memory usage. It's the recommended approach for new development. - **Expected Insights:** Expected to be faster and more memory-efficient than Newtonsoft.Json, reflecting improvements in the .NET serialization capabilities. 3. **Protobuf (Google.Protobuf):** - **Purpose:** Tests the serialization performance using Protocol Buffers, a language-neutral, platform-neutral, extensible way of serializing structured data. - **Importance:** Protobuf is known for its efficiency and speed, making it suitable for high-performance applications and microservices. - **Expected Insights:** Likely to show significant performance and size benefits over JSON serialization, due to its binary format and efficient data representation. 4. **Avro:** - **Purpose:** Measures the serialization performance using Apache Avro, a binary serialization format. - **Importance:** Avro is designed for data serialization in a compact binary format, which is beneficial for both data storage and RPC. It supports schema evolution. - **Expected Insights:** Expected to offer compact serialization with performance characteristics similar to or better than Protobuf, especially in scenarios involving schema evolution. 5. **MessagePack:** - **Purpose:** Tests serialization using MessagePack, a binary format that is more efficient than JSON. - **Importance:** MessagePack can be used to reduce payload size and improve serialization/deserialization speed, making it suitable for performance-critical applications. - **Expected Insights:** Should demonstrate advantages over JSON in terms of size and speed, potentially offering a middle ground between JSON and Protobuf/Avro in terms of readability and efficiency. 6. **Bson (MongoDB.Bson):** - **Purpose:** Measures the serialization performance to BSON, a binary representation of JSON-like documents. - **Importance:** BSON is used in MongoDB and is designed to be efficient in space and speed, supporting embedded documents and arrays. - **Expected Insights:** Expected to show performance characteristics similar to JSON but with potential benefits in scenarios where MongoDB is used as the data store. ### Conclusion Running these benchmarks will provide insights into the trade-offs between different serialization methods in terms of speed, memory usage, and payload size. This can guide the selection of an appropriate serialization format based on the specific requirements of an application, such as the need for compact representation, speed of serialization/deserialization, or compatibility with certain data stores or transport protocols.


Benchmark Comments: