You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何仅将Protobuf序列化为byte[]?JSON与Protobuf序列化基准测试

问题:实现JSON与Protobuf序列化的公平基准测试

我正在为明天的演示编写一个基准测试,用于对比JSON序列化与Protobuf序列化的速度。现有测试代码如下:

using System.Text;
using System.Text.Json;
using AutoFixture;
using BenchmarkDotNet.Attributes;
using ProtobufDemo.ProtoModels;
using Google.Protobuf;

namespace Benchmark;

public class JsonVsProtoBenchmark
{
    private readonly AddressBook _addressBook;

    public JsonVsProtoBenchmark()
    {
        Fixture fixture = new();
        _addressBook = fixture.Create<AddressBook>();
    }

    [Benchmark]
    public void SerialiseToJsonOnly() => JsonSerializer.Serialize(_addressBook);

    [Benchmark]
    public void SerialiseToJsonEncodeAndWrite() // This is more representative of what the protobuf serialiser is actually doing
    {
        string json = JsonSerializer.Serialize(_addressBook);
        byte[] encodedBytes = Encoding.UTF8.GetBytes(json);
        Stream.Null.Write(encodedBytes);
        Stream.Null.Flush();
    }

    [Benchmark]
    public void SerialiseToProtobufAndWrite() => _addressBook.WriteTo(Stream.Null); // Note we have an extra write here that's not necessarily fair
}

但这并非公平测试,因为IMessage.WriteTo(Stream)除了序列化还执行大量额外操作,其底层实现如下:

public static void WriteTo(this IMessage message, Stream output)
{
    ProtoPreconditions.CheckNotNull(message, "message");
    ProtoPreconditions.CheckNotNull(output, "output");
    CodedOutputStream codedOutput = new CodedOutputStream(output);
    message.WriteTo(codedOutput);
    codedOutput.Flush();
}

我已通过SerialiseToJsonEncodeAndWrite()测试尝试弥补部分差异,但仍未实现对等对比。请问是否有方法仅执行Protobuf的序列化步骤,而非通过.WriteTo()方法?


解决方案
  • 纯序列化操作(对应SerialiseToJsonOnly):使用IMessage.ToByteArray()方法,该方法直接执行Protobuf核心序列化逻辑,生成字节数组,没有WriteTo(Stream)中的参数检查、Stream包装和Flush操作,与JSON序列化到字符串的操作完全对等。
  • 包含写入操作的场景(对应SerialiseToJsonEncodeAndWrite):直接用ToByteArray()生成字节数组后写入流,和JSON的步骤一一对应,避免WriteTo(Stream)的额外开销。

修改后的基准测试代码示例:

using System.Text;
using System.Text.Json;
using AutoFixture;
using BenchmarkDotNet.Attributes;
using ProtobufDemo.ProtoModels;
using Google.Protobuf;

namespace Benchmark;

public class JsonVsProtoBenchmark
{
    private readonly AddressBook _addressBook;

    public JsonVsProtoBenchmark()
    {
        Fixture fixture = new();
        _addressBook = fixture.Create<AddressBook>();
    }

    [Benchmark]
    public string SerialiseToJsonOnly() => JsonSerializer.Serialize(_addressBook);

    [Benchmark]
    public void SerialiseToJsonEncodeAndWrite()
    {
        string json = JsonSerializer.Serialize(_addressBook);
        byte[] encodedBytes = Encoding.UTF8.GetBytes(json);
        Stream.Null.Write(encodedBytes);
        Stream.Null.Flush();
    }

    // 纯序列化操作,与SerialiseToJsonOnly对等
    [Benchmark]
    public byte[] SerialiseToProtobufOnly() => _addressBook.ToByteArray();

    // 序列化+写入操作,与SerialiseToJsonEncodeAndWrite对等
    [Benchmark]
    public void SerialiseToProtobufEncodeAndWrite()
    {
        byte[] protoBytes = _addressBook.ToByteArray();
        Stream.Null.Write(protoBytes);
        Stream.Null.Flush();
    }
}

补充说明:

  • ToByteArray()的底层逻辑是预先计算序列化后的字节大小,创建对应长度的数组,再通过CodedOutputStream写入数组,全程没有额外的Stream操作,完全聚焦序列化本身。
  • 如果想完全模拟WriteTo(CodedOutputStream)的纯序列化步骤(不生成字节数组),也可以手动创建CodedOutputStream写入到MemoryStream,但ToByteArray()已经是最简洁的等价实现,适合基准测试场景。

内容的提问来源于stack exchange,提问作者ScottishTapWater

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 02:45:18