三段序列化代码的测试:比较protocol buffers的CodedOutputStream和java自带的DataOutputStream

标签: java | 发表时间:2011-10-19 18:54 | 作者:snnn119 ndv
出处:http://snnn.sinaapp.com

最近一段时间在写一个小东西,一个很简单k-v数据库。我并没有像MyISAM那样把每个表放在一个单独的文件中,而是用一个总的大文件来放所有的表。(类似于InnoDB默认的方式)。我需要在这个硕大无比的文件的开头放一个map,key是表名,value是这个表的第一个页在此文件中的偏移地址。即这样一个结构:Map < String, Long > headers。那么我就需要为这个Map写一个序列化方法,把它从Object转化成byte[]。写完第一个实现,并用junit测试完正确性之后,我准备再写2个实现,测试下性能。

三种实现的思路分别是:
1、用google protocol buffers的CodedOutputStream,手写序列化。先计算序列化之后需要多大空间,然后new出这个byte[],然后往里填。这是protoc生成的代码所采用的方式。
2、先new一个ByteArrayOutputStream,然后用它构造一个DataOutputStream,然后往里写,最后用ByteArrayOutputStream的toByteArray返回。其中字符串以UTF8的方式写入。
3、先new一个ByteArrayOutputStream,然后用它构造一个CodedOutputStream,手写序列化,最后用ByteArrayOutputStream的toByteArray返回。

ByteArrayOutputStream的默认buffer大小是32字节,如果DataOutputStream/CodedOutputStream往里面写的时候遇到它满了,就需要对现有的内存做一次copy来grow一下。这就是为什么我首先写的是方案一。但是方案一的缺点是,它需要把这个Map遍历2次。

测试环境:Core i3-2100,8GB内存,sun jdk 7。google protocol buffers的版本是2.4.1。
测试方式:首先往这个hashmap里面添1000条记录,key是长度为10的随机字符串,value是64位随机整数(0×0-0x7fffffffffffffffL之间均匀随机)。先warm up一下,然后执行1000次序列化方法。
测试结果:
方案1执行1000次花费时间=170ms-180ms左右。
方案2执行1000次花费时间=75ms-95ms左右。
方案3执行1000次花费时间=105ms-110ms左右。
序列化后的长度在20000到21000字节左右。google protocol buffers的最终码长看不出明显优势,甚至略差于DataOutputStream,这个比较符合我的推测,因为这种情况下,CodedOutputStream的变长编码方式发挥不出来优势。

方案1的代码:
public byte[] serialize(Object obj) throws IOException {
Map data=(Map)obj;
int size=CodedOutputStream.computeInt32SizeNoTag(data.size());
for(Map.Entry e:data.entrySet()){
size+=CodedOutputStream.computeStringSizeNoTag(e.getKey());
size+=CodedOutputStream.computeInt64SizeNoTag(e.getValue());
}
byte[] ret=new byte[size];
CodedOutputStream cos=CodedOutputStream.newInstance(ret);
cos.writeInt32NoTag(data.size());
for(Map.Entry e:data.entrySet()){
cos.writeStringNoTag(e.getKey());
cos.writeInt64NoTag(e.getValue());
}

//这句代码其实不必要
cos.flush();

return ret;
}
方案2的代码:
public byte[] serialize(Object obj) throws IOException {
Map data=(Map)obj;
ByteArrayOutputStream baos= new ByteArrayOutputStream();
DataOutputStream oos=new DataOutputStream(baos);
oos.write(data.size());
for(Map.Entry e:data.entrySet()){
oos.writeUTF(e.getKey());
oos.writeLong(e.getValue());
}

oos.flush();
return baos.toByteArray();
}
方案2的代码:
public byte[] serialize(Object obj) throws IOException {
Map data=(Map)obj;
ByteArrayOutputStream baos= new ByteArrayOutputStream();
CodedOutputStream cos=CodedOutputStream.newInstance(baos);
cos.writeInt32NoTag(data.size());
for(Map.Entry e:data.entrySet()){
cos.writeStringNoTag(e.getKey());
cos.writeInt64NoTag(e.getValue());
}

cos.flush();
return baos.toByteArray();
}

结论:Sometimes,Simple is the best。 这是一个很特殊的场景,所以测试结果说明不了什么问题,我只是因为最近看了一些关于如何做java code的benchmark的文章,实践一下那些方法。但是始终来说,Google protocol buffers对我最大的诱惑不是执行效率也不是最终码长,而是前后兼容。没有什么file format、protocol defines是一成不变的,对互联网产品,灵活比其它这2个都重要。

相关 [序列化 代码 测试] 推荐:

三段序列化代码的测试:比较protocol buffers的CodedOutputStream和java自带的DataOutputStream

- ndv - snnn的blog
最近一段时间在写一个小东西,一个很简单k-v数据库. 我并没有像MyISAM那样把每个表放在一个单独的文件中,而是用一个总的大文件来放所有的表. (类似于InnoDB默认的方式). 我需要在这个硕大无比的文件的开头放一个map,key是表名,value是这个表的第一个页在此文件中的偏移地址. 即这样一个结构:Map < String, Long > headers.

无代码化的测试自动化

- -
2020年软件测试自动化的趋势除了智能化、云化、敏捷化/DevOps化、模型化等,还有一个亮眼的存在:Codeless Test Automation,即无代码化的测试自动化. 不是没有代码,而是测试人员不用自己开发测试代码,使用Codeless测试工具可以帮助我们生成可以执行的测试用例集. 如此将大大降低自动化测试的技术门槛,没有编程经验的测人员甚至是业务分析人员也可以很快上手,是不是令人心动.

(反)序列化

- - Java - 编程语言 - ITeye博客
本章关注对象序列化API,它提供了一个框架,用来将对象编码成字节流,并从字节流中重新构建对象. “将对象编码成字节流”被称作对象序列化,相反的处理过程被称作反序列化. 序列化技术为远程通信提供了标准的线路级对象表示法,也为JavaBeans组件结构提供了标准的持久化数据格式. 第七十四条:谨慎地实现Serializable接口.

java序列化java.io.Externalizable

- - Java - 编程语言 - ITeye博客
这次我们讲的是控制对象的序列化和反序列化. 控制序列化就是有选择的序列化对象,而不是把对象的所以内容都序列化,前篇我们的例子中介绍了transit变量和类变量(static)不被序列化,现在我们还有一种更为灵活的控制对象序列化和反序列方法,可以在序列化过程中储存其他非this对象包含的数据. 我们现在再来介绍一个接口 java.io.Externalizable.

18 款在线代码片段测试工具

- - ITeye资讯频道
本文作者 Steve Smith是网站 DesignDrizzle的创始人,他是一位工作超过7年的专业网站设计者. 在这篇文章里展示了一些比较有价值、用起来非常方便的代码片段检测工具. 这些工具将大大的提升开发者在代码片段检测方面的工作效率,亦可确保他们的代码运行起来万无一失. 许多开发者都使用在线代码片段检测工具,这些工具当中有些是允许开发者和别人一起编辑/分享相互之间的代码,分享的目的主要是共同分析代码并相互协助完成代码片段的检查.

java序列化与反序列化以及浅谈一下hadoop的序列化

- - CSDN博客云计算推荐文章
1、什么是序列化和反序列化. 神马是序列化呢,序列化就是把 内存中的对象的状态信息,转换成 字节序列以便于存储(持久化)和网络传输. (网络传输和硬盘持久化,你没有一定的手段来进行辨别这些字节序列是什么东西,有什么信息,这些字节序列就是垃圾). 反序列化就是将收到 字节序列或者是硬盘的持久化数据,转换成 内存中的对象.

JAVA 反序列化攻击

- - OneAPM 博客
Java 反序列化攻击漏洞由. FoxGlove 的最近的一篇博文爆出,该漏洞可以被黑客利用向服务器上传恶意脚本,或者远程执行命令. 由于目前发现该漏洞存在于 Apache commons-collections, Apache xalan 和 Groovy 包中,也就意味着使用了这些包的服务器(目前发现有WebSphere, WebLogic,JBoss),第三方框架(Spring,Groovy),第三方应用(Jenkins),以及依赖于这些服务器,框架或者直接/间接引用这些包的应用都会受到威胁,这样的应用的数量会以百万计.

【外刊IT评论】你真正需要的代码测试覆盖率是多少?

- Ryan - 外刊IT评论网
本文是从 How much code coverage do you really need. 我写这篇文章的起因是由于看了@unclebobmartin在微博上的一些看起来言之凿凿的话语. 给那些不认识Uncle Bob的人介绍一下——他是我们软件产业里最著名的一个专家,是《 Clean Code(代码整洁之道)》这本著作的作者,是敏捷宣言(Agile Manifesto)的签署人之一.

测试版 Chrome 支持在浏览器内执行 C/C++ 代码

- 洋白菜 - 36氪
Google 一直在研究一个叫 Native Client 的东西,可以让 Web 应用在浏览器内执行编译过的本地代码,现在这个功能已经整合进了 Chrome 浏览器内. Google 在 Chrome 博客中说,最新 Beta 版的 Chrome 浏览器(版本 14)将支持 Native Client,允许在浏览器内执行 C/C++ 代码,就像现在浏览器能够执行 JavaScript 代码一样.

Dabblet,简洁实用的HTML和CSS代码段在线测试工具

- - 36氪
跨浏览器兼容,对前端攻城师们来说是一个不得不处理的问题. 为了在浏览器间呈现统一的显示效果,攻城师们不仅要为每个游览器添加CSS前缀,甚至还需要用到一些特殊的CSS Hack技巧. 于是, jsFiddle、 JSBin等前端代码的在线测试工具应运而生. 然而,使用jsFiddle仍有很多 不便之处.