.NET性能优化-是时候换个序列化协议了( 三 )

MessagePack-CSharp,同样也是引入一个Nuget包:
<PackageReference Include="MessagePack" Version="2.4.35" />然后在类上只需要打一个MessagePackObject的特性,然后在需要序列化的属性打上Key特性:
[MessagePackObject]public partial class DemoClass{[Key(0)] public int P1 { get; set; }[Key(1)] public bool P2 { get; set; }[Key(2)] public string P3 { get; set; } = null!;[Key(3)] public double P4 { get; set; }[Key(4)] public long P5 { get; set; }}使用起来也非常简单,直接调用MessagePack提供的静态类即可:
using MessagePack;// Serialize[MethodImpl(MethodImplOptions.AggressiveInlining)]public static byte[] MessagePack<T>(T origin){return global::MessagePack.MessagePackSerializer.Serialize(origin);}// Deserializepublic T MessagePack<T>(byte[] bytes){return global::MessagePack.MessagePackSerializer.Deserialize<T>(bytes);}另外它提供了Lz4算法的压缩程序,我们只需要配置Option,即可使用Lz4压缩,压缩有两种方式,Lz4BlockLz4BlockArray,我们试试:
public static readonly MessagePackSerializerOptions MpLz4BOptions =MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4Block);// Serialize[MethodImpl(MethodImplOptions.AggressiveInlining)]public static byte[] MessagePackLz4Block<T>(T origin){return global::MessagePack.MessagePackSerializer.Serialize(origin, MpLz4BOptions);}// Deserializepublic T MessagePackLz4Block<T>(byte[] bytes){return global::MessagePack.MessagePackSerializer.Deserialize<T>(bytes, MpLz4BOptions);}MemoryPack这里也是Yoshifumi Kawai大佬实现的MemoryPack,同样也是引入一个Nuget包,不过需要注意的是,目前需要安装VS 2022 17.3以上版本和.NET7 SDK,因为MemoryPack代码生成依赖了它:
<PackageReference Include="MemoryPack" Version="1.4.4" />使用起来应该是这几个二进制序列化协议最简单的了,只需要给对应的类加上partial关键字,另外打上MemoryPackable特性即可:
[MemoryPackable]public partial class DemoClass{public int P1 { get; set; }public bool P2 { get; set; }public string P3 { get; set; } = null!;public double P4 { get; set; }public long P5 { get; set; }}序列化和反序列化也是调用静态方法:
// Serialize[MethodImpl(MethodImplOptions.AggressiveInlining)]public static byte[] MemoryPack<T>(T origin){return global::MemoryPack.MemoryPackSerializer.Serialize(origin);}// Deserializepublic T MemoryPack<T>(byte[] bytes){return global::MemoryPack.MemoryPackSerializer.Deserialize<T>(bytes)!;}它原生支持Brotli压缩算法,使用如下所示:
// Serialize[MethodImpl(MethodImplOptions.AggressiveInlining)]public static byte[] MemoryPackBrotli<T>(T origin){using var compressor = new BrotliCompressor();global::MemoryPack.MemoryPackSerializer.Serialize(compressor, origin);return compressor.ToArray();}// Deserializepublic T MemoryPackBrotli<T>(byte[] bytes){using var decompressor = new BrotliDecompressor();var decompressedBuffer = decompressor.Decompress(bytes);return MemoryPackSerializer.Deserialize<T>(decompressedBuffer)!;}跑个分吧我使用BenchmarkDotNet构建了一个10万个对象序列化和反序列化的测试,源码在末尾的Github链接可见,比较了序列化、反序列化的性能,还有序列化以后占用的空间大小 。
public static class TestData{//public static readonly DemoClass[] Origin = Enumerable.Range(0, 10000).Select(i =>{return new DemoClass{P1 = i,P2 = i % 2 == 0,P3 = $"Hello World {i}",P4 = i,P5 = i,Subs = new DemoSubClass[]{new() {P1 = i, P2 = i % 2 == 0, P3 = $"Hello World {i}", P4 = i, P5 = i,},new() {P1 = i, P2 = i % 2 == 0, P3 = $"Hello World {i}", P4 = i, P5 = i,},new() {P1 = i, P2 = i % 2 == 0, P3 = $"Hello World {i}", P4 = i, P5 = i,},new() {P1 = i, P2 = i % 2 == 0, P3 = $"Hello World {i}", P4 = i, P5 = i,},}};}).ToArray();public static readonly DemoClassProto.DemoClassArrayProto OriginProto;static TestData(){OriginProto = new DemoClassArrayProto();for (int i = 0; i < Origin.Length; i++){OriginProto.DemoClass.Add(DemoClassProto.DemoClassProto.Parser.ParseJson(JsonSerializer.Serialize(Origin[i])));}}}

经验总结扩展阅读