如何仅将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
相关产品推荐
相关产品推荐

