旧地址:https://www.shisujie.com/blog/Google-ProtoBuf-vs-ProtoBuf-Net
Protocol Buffers - Google官方教程文档
示例源代码
什么是protobuf?
protobuf全称Protocol Buffers,由Google推出的一种平台、语言无关的数据交互格式。
为什么用protobuf
由于公司项目开发中,使用了WCF,其默认数据序列化是DataContractSerializer,采用xml格式,考虑到性能提升,于是采用了protobuf。
比较选择
protobuf的.net实现主要有两个版本:
Google.ProtoBuf 如何使用
- 安装Nuget包:Google.Protobuf和Google.Protobuf.Tools
1 2 3 4 5 6 7 8 9 10 11
| <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Google.Protobuf" Version="3.12.3" /> <PackageReference Include="Google.Protobuf.Tools" Version="3.12.3" /> </ItemGroup> </Project>
|
类似于.net中的类结构描述
1 2 3 4 5 6 7 8 9 10
| syntax = "proto3";
package Jess.Sample.GoogleProtoBuf;
message Test { int32 ID = 1; string Name = 2; }
|
- 基于上面定义的proto文件,生成相应的C#类文件
命令行中执行如下代码:
1 2 3
| <自己的protoc.exe文件目录>protoc.exe --proto_path=<proto文件所在目录> --csharp_out=<proto生成的cs文件目录> <定义的proto文件名>
|
或者参考如下powershell脚本内容:
将脚本内容保存到项目目录下,直接执行ps1文件。
1 2 3 4 5 6 7 8 9 10
|
$scriptpath = $PSScriptRoot cd $scriptpath
$userpath=$env:USERPROFILE
&$userpath'\.nuget\packages\google.protobuf.tools\3.12.3\tools\windows_x64\protoc.exe' --proto_path=./protofile --csharp_out=./protoclass test.proto
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public void SimplestUse() { Test test_forProtobuf = new Test(); test_forProtobuf.ID = 2; test_forProtobuf.Name = "测试 Google.ProtoBuf";
using (MemoryStream memoryStream = new MemoryStream()) { test_forProtobuf.WriteTo(memoryStream); } }
|
更多使用示例,见本文开头源码链接。
protobuf-net 如何使用
1 2 3 4 5 6 7 8 9 10
| <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="protobuf-net" Version="3.0.29" /> </ItemGroup> </Project>
|
- protobuf-net作者同时提供了Protogen工具,可以直接使用.proto文件生成以下类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| using ProtoBuf;
namespace Jess.Sample.ProtoBufNet { [ProtoContract] public class Test { [ProtoMember(1)] public int ID { get; set; }
[ProtoMember(2)] public string Name { get; set; } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public void SimplestUse() { Test test_forProtobuf = new Test(); test_forProtobuf.ID = 1; test_forProtobuf.Name = "测试 protobuf-net";
using (MemoryStream memoryStream = new MemoryStream()) { ProtoBuf.Serializer.Serialize(memoryStream, test_forProtobuf); } }
|
更多使用示例,见本文开头源码链接。
Google.ProtoBuf 和 protobuf-net 性能比较
性能测试工具使用:BenchmarkDotNet
直接传入MemoryStream的WriteTo扩展方法,其实是官方内部封装了CodedOutputStream —— 从性能数据上看,也是无差别的。
1 2 3 4 5 6 7
| BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18362.900 (1903/May2019Update/19H1) Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores [Host] : .NET Framework 4.8 (4.8.4180.0), X86 LegacyJIT
Toolchain=InProcessEmitToolchain
|
Method |
Mean |
Error |
StdDev |
Median |
Rank |
Google.ProtoBuf使用MemoryStream |
789.7 ns |
15.72 ns |
46.36 ns |
775.3 ns |
2 |
Google.ProtoBuf使用MemoryStream嵌套CodedOutputStream |
799.6 ns |
16.14 ns |
46.81 ns |
791.6 ns |
2 |
protobuf-net使用MemoryStream |
491.7 ns |
7.13 ns |
6.32 ns |
488.8 ns |
1 |
1 2 3 4 5 6 7
| BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18362.900 (1903/May2019Update/19H1) Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores [Host] : .NET Framework 4.8 (4.8.4180.0), X86 LegacyJIT
Toolchain=InProcessEmitToolchain
|
Method |
Mean |
Error |
StdDev |
Rank |
Google.ProtoBuf使用FileStream |
460.3 μs |
14.65 μs |
43.18 μs |
1 |
protobuf-net使用FileStream |
457.9 μs |
15.37 μs |
45.31 μs |
1 |
1 2 3 4 5 6 7
| BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18362.900 (1903/May2019Update/19H1) Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores [Host] : .NET Framework 4.8 (4.8.4180.0), X86 LegacyJIT
Toolchain=InProcessEmitToolchain
|
Method |
Mean |
Error |
StdDev |
Rank |
Google.ProtoBuf使用FileStream |
249.8 μs |
4.97 μs |
10.15 μs |
1 |
protobuf-net使用FileStream |
260.5 μs |
4.84 μs |
9.88 μs |
2 |
上方是简易数据结构性能,下方是相对复杂一些的数据结构性能
注意事项【一些坑】:
- proto3移除了required,对于required的作用,其实争议很大。proto3更加追求极简。
- 但在我的之前遇到的情况,bool和enum未赋值的情况下,序列化时,默认值是有问题的。所以,单纯.net环境下,目前还是推荐proto2吧。
_
:proto文件中下划线表示下划线后一个字母为大写。生成的类中,属性是无法带有下划线的。
- 官方建议属性定义均用小写。
1 2 3 4 5 6 7
| BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18362.900 (1903/May2019Update/19H1) Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores [Host] : .NET Framework 4.8 (4.8.4180.0), X86 LegacyJIT
Toolchain=InProcessEmitToolchain
|
Method |
Mean |
Error |
StdDev |
Rank |
Google.ProtoBuf使用FileStream |
470.3 μs |
17.45 μs |
51.47 μs |
1 |
protobuf-net使用FileStream |
466.9 μs |
14.68 μs |
43.28 μs |
1 |
1 2 3 4 5 6 7
| BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18362.900 (1903/May2019Update/19H1) Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores [Host] : .NET Framework 4.8 (4.8.4180.0), X86 LegacyJIT
Toolchain=InProcessEmitToolchain
|
Method |
Mean |
Error |
StdDev |
Rank |
Google.ProtoBuf使用FileStream |
281.3 μs |
7.42 μs |
21.88 μs |
1 |
protobuf-net使用FileStream |
283.1 μs |
5.64 μs |
13.29 μs |
1 |
由此可见,两个库直接性能差距并不是很大,鉴于个人的使用场景,更加推荐protobuf-net。
掘:奇葩史