July 9, 2008
Protocol Buffers 性能测试
昨天大致介绍了下 Protocol Buffers,由于时间关系没能做性能测试,今天上午补上,给 Protocol Buffers 做一个性能测试。
声明:转载请务必以超级链接的形式注明本文地址(http://hellobmw.com/archives/protocol-buffers-performance.html)。
细节就不讲了,测试程序是在电话本基础上改的,往一个 AddressBook 中插入 100000 条 Person 联系人记录,每个联系人有 1 个整型的 ID,1 个 std::string 型名称,还有 10 个 std::string 型电话号码。分别使用 debug 版本、release 版本、Protocol Buffers 速度优化模式(参见 Getting More Speed 部分)的 release 版本进行测试,每次测试前将之前生成的 addressbook 文件删除。
测试结果截图如下(时间单位为毫秒):
Protocol Buffers 性能测试 debug 版本:

Protocol Buffers 性能测试 release 版本:

Protocol Buffers 性能测试 速度优化模式的 release 版本:

很显然 Protocol Buffers 的序列化、反序列化速度令人相当满意,实现了号称的“比 XML 快 20 到 100 倍”。
另外需要说明的是,Protocol Buffers 生成的数据文件体积非常小,基本上没有任何不必要的冗余,在这个测试程序中,每条记录的数据内容占用空间应该在 200 字节左右,而存有 100000 条记录的文件大小也仅为 23,472,378 字节,比值差不多刚好 100000。XML 跟这个没什么好比的吧,呵呵。
测试做得比较简单,希望高手给予批评指导。

Jiang at 11:58 Jul 09, 2008 ₪
北极,你没有交代这100000条数据是全部先保存在memory中最后一次性写入hard drive还是每插入一条数据就写入一次。不过根据你给出的processing time,应该是一次性写入。单就efficiency来讲,protocol buffer和那种raw in-memory data structures 写入binary file相比没有什么优势,它牺牲了一些performance换来了其他的一些好处,而且性价比很高:)
北极冰仔 at 12:25 Jul 09, 2008 ₪
@Jiang 是一次性写入的。我对这方面还不是很了解,能详细讲一下与 raw in-memory 相比 protocol buffers 的优势吗,谢谢。^_^
Jiang at 14:44 Jul 09, 2008 ₪
如果但就性能来说,单纯的写binary file(fopen, fclose,etc)应该是最好的,但是这种方法最大的问题是跨平台。因为在不同的平台下,相同的数据类型可能会有不同的memroy layout或是字节长度。例如,在windows下,宽字符类型wchar的字节数是16,但是*nix下是32.所以当一个binary文件被跨平台读写时,就很有可能发生错误,读出的数据不正确,另外这种方法还有一个问题就是造成在空间上的浪费,没有办法,要想效率高,就只能用定长来减少在解析数据上消耗的时间。再有一点就是数据的扩展性,这一点对于raw in memory很困难,因为缺少标记或protocol。
protocol buffer解决了以上的问题通过给buffer添加protocol,不同的平台下,不同版本的数据,只要遵守protocol就能够正确读写。但是这不可能是免费的,效率是一定会受到影响。先简单说这么多吧:)
北极冰仔 at 16:03 Jul 09, 2008 ₪
@Jiang 十分感谢,解释地相当清晰。我访问了你的博客,猜想你可能是一名在这个领域有研究的博士,很希望能在你的博客上读到更多这方面的内容。^_^
Jiang at 16:49 Jul 09, 2008 ₪
呵呵,我是做数据库的,一直都很喜欢编程,c/c++。以后多多交流:)
北极冰仔 at 18:17 Jul 09, 2008 ₪
@Jiang 我要多多向你学习^_^
David at 22:40 Jul 09, 2008 ₪
个人认为除非是要做rpc这种对数据的结构行要求很高的东西,用这个protocol buffer比较好,否则的话简单的进行text化存储岂不更好,
比如上面的电话本的数据就可以存储为
id=00000&phone=123456&phone=123456…;id=00000&phone=123456&phone=123456…;id=00000&phone=123456&phone=123456…;
这样存储简单,解析也不会很困难,欢迎指正,呵呵!
北极冰仔 at 08:48 Jul 10, 2008 ₪
@David 在相对较小数据量、数据结构比较稳定的情况下,使用文本化存储或采用XML应该都是不错的。个人看法。^_^
is at 09:32 Jul 10, 2008 ₪
to david:
实际写程序就知道了,简单的text读写只是不很困难,而protobuf是非常方便。
北极冰仔 at 09:44 Jul 10, 2008 ₪
@is 欢迎!
David at 22:36 Jul 10, 2008 ₪
protocal buffer是很方便,但其生成的文件不具有子解释性(相比xml),且数据文件的通用性不是很强(比如我上面说的那种格式的数据文件可以很容易的使用脚本处理后直接导入DB中).
—唱唱反调,呵呵,思考下protocal buffer在实际工作中的应用场景。
hail at 10:39 Jul 23, 2008 ₪
lz,我对Protocol Buffers 还不怎么了解,我现在手头上有个项目使用java在开发。由于这个项目设计到许多参数,所以必须有专门的参数文件对其进行管理,为了跨平台等要求我现在采用的是XML作为参数文件的存储格式,但是,我们这个项目的参数文件可能达到上百兆,当数据很大时XML解析太慢了,甚至不能工作。参数文件的格式采用XML分层形式来描述,可能有几千层,每层中的参数大致都差不多,大致为:
…..
…..
.
.
.
…..
我想知道对于这样的情况Protocol Buffers 能处理吗?
donald at 14:24 Aug 14, 2008 ₪
你好,我问一下你的“Protocol Buffers 性能测试 debug 版本:”中的debug版是怎么回事?是配置出的还是下载的debug版?
北极冰仔 at 16:42 Aug 14, 2008 ₪
@donald 是编译的配置。
wanghongxia at 13:53 Aug 21, 2008 ₪
您好,我对‘protocol buffer’比较感兴趣,但是也不是很了解。请问您文中提到的‘很显然 Protocol Buffers 的序列化、反序列化速度令人相当满意,实现了号称的“比 XML 快 20 到 100 倍”。’这句话是什么意思。
比如:从一个.proto文件提取数据是不是就是所谓的序列化啊? 需要用到parsefrom()函数。
北极冰仔 at 14:45 Aug 21, 2008 ₪
@wanghongxia 序列化和反序列化的定义我就不说了,用不准确的例子说来序列化就是将数据存为protobuf的格式,反序列化就是从protobuf生成的数据中读出你要的信息。纠正一下,.proto文件只是用来描述结构化数据的IDL文件,并不是真正存储数据的文件。
Jiang at 15:48 Aug 21, 2008 ₪
对象的序列化主要是用在网络传输上,有时我们需要把对象通过网络传输到另外的系统平台下,因此就需要将对象序列化,也就是将其转化为一系列的字节传输到另一端,另一端在拿到这一些列字节后再将其反序列化,也就是重新组装为对象。这样对象就可以继续被使用了。在这其中,效率就是一个非常重要的因素。
北极冰仔 at 16:07 Aug 21, 2008 ₪
@Jiang 学习了。我刚才解释的也太不准确了,惭愧。-__-||
wanghongxia at 16:59 Aug 21, 2008 ₪
谢谢大家的解答。 @北极冰仔说‘.proto文件只是用来描述结构化数据的IDL文件,并不是真正存储数据的文件。’,那存储数据的文件类型是可以随便的,比如文本文件?
我自己做过一个测试,我用DOM 来解析xml文件,同时用protocol buffer解析它所对应的文件, 但是并不存在“比 XML 快 20 到 100 倍”啊?
这个是什么原因呢?
请大家给个解释吧?
北极冰仔 at 17:19 Aug 21, 2008 ₪
数据是以二进制存储的。你可以用DOM解析含有十万条电话本记录的XML试试。在数据量很小的情况下,XML解析起来也不会很慢,但是对于很大的数据量,XML就没什么优势可言了,占用空间大,解析速度慢。尤其是使用DOM,需要一次性读入整棵树。
Jiang at 17:26 Aug 21, 2008 ₪
当然不可能是文本文件,否则这东西也就没什么意义了,而解析也不用google给我们的接口,我们自己就可以。它肯定是一种二进制文件,但是其中的bits排列遵守某种protocol,这样在space上就会非常紧凑,文件肯定比文本文件小很多,具体什么样的protocol,不知道google有没有发布。
至于解析速度,决定的因素有很多,首先最直接的一点就是数据的大小和结构,你可以尝试用dom解析一个上百M的xml文件试试看,就知道那个效率的确很差,虽然我没有用过protocol buffer,但是我想效率应该会有比较大的提升。
wanghongxia at 17:41 Aug 21, 2008 ₪
为了测试xml解析和protocol buffer 解析的效率。我用C# 做了测试。
我在内存输入一数组,然后把数组拼成xml 格式并保存为.xml文件; 同时同样的数组 为protocol buffer 对象赋值并保存为文件。
保存后的XML 文件是36M ,protocol buffer 文件是10M左右。
然后我用DOM 方法解析XML 文件,并把数据显示出来,用protocol buffer 生成的类对象解析对应文件同时显示数据;
结果是DOM 的耗时比protocol buffer 还要小?
请问我是哪里出现了错误? 是用这种方式来测试XML 和 protocol buffer 的解析效率吗?
北极冰仔 at 18:36 Aug 21, 2008 ₪
好像目前PB还不支持.net吧?你是如何用C#做测试的呢?可否把你的XML结构和.proto贴出来看看,我觉得你可能没用对,36M的XML存成二进制的PB居然还有10M大小。
wanghongxia at 09:35 Aug 22, 2008 ₪
google 开发人员已经开发出来pb 的c# 版本。
http://github.com/jskeet/dotnet-protobufs/tree/master
我构造的proto结构如下:
// use the tag creator replace with the tag dc:creator;
package tutorial;
//option java_package = “com.example.tutorial”;
//option java_outer_classname = “AddressBookProtos”;
option csharp_namespace = “Tutorial”;
option csharp_file_classname = “Channelprotos”;
option csharp_multiple_files = false;
option csharp_nest_classes = false;
option csharp_public_classes = true;
message Item{
required string title = 1;
required string description = 2;
required string link = 3;
optional string pubDate = 5;
}
message Channel{
required string title = 1;
required string link = 3;
required string description = 2;
optional string pubDate = 5;
optional string lastBuildDate = 6;
optional string generator = 7;
repeated Item item = 8;
}
xml 结构:
……..
在测试时我就是把一个已知数组分别赋值给这两种结构的对象,最后分别保存为文件。然后我在对这两种文件分别用dom 和 pb 的方法解析,可是效率并不是像你们所说的,反而是dom 的快些呢?
请帮我指点指点,我是不是哪里理解有错误啊?
Chairmin at 13:50 Aug 29, 2008 ₪
感觉应该是不错的,打算在网络通信中使用,不知道在消息数量比较多但XML数据段比较小的情况下,优势能有多少?