论坛首页 Java企业应用论坛

实际项目数据下的序列化性能对比:PHPRPC vs Hessian2 vs AMF3

浏览 14601 次
精华帖 (0) :: 良好帖 (13) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-03-17   最后修改:2009-03-18
(最新测试结果见第二页:加入AMF3)
实际项目中的一个企业信息表:15072条记录,测试结果令人吃惊:Hessian胜出太多,不知andot是怎么看,测试过程有误?
测试过程很简单:读取数据表全部数据至List实例,然后序列化,并将数据保存至本地硬盘。

测试环境:Oracle10,jdk1.5,3G内存。
测试结果:
引用

开始测试,序列化后二进制数据写进本地文件...
测试记录总数:15072条

Hessian所用时间与空间:829毫秒,4665088字节。

PHPRPC所用时间与空间:17152毫秒,190715025字节。

结束测试


数据表Oracle统计信息:
引用

NUM_ROWS:15008
AVG_ROW_LEN:207


数据表记录vo字段代码:
    private String imposedCorporationId;
    private String corpCode;
    private String siRegisterCardCode;
    private String taxMgtCode;
    private String orgCode;
    private String businessRegCode;
    private String corpName;
    private String corpAbbr;
    private String postalCode;
    private String corpAddr;
    private String corpLp;
    private String lpIdCardNum;
    private String lpPhone;
    private String principal;
    private String principalPhone;
    private String financialPrincipal;
    private String financialPhone;
    private String lcLinkman;
    private String lcPhone;
    private String otherPhone;
    private String corpSmsPhone;
    private String corpBankDdv;
    private String corpBankAccount;
    private String corpBank2Ddv;
    private String corpBank2Account;
    private String imposingDeptDdv;
    private String mgtDeptDdv;
    private String corpStatusDdv;
    private String payAbilityDdv;
    private String corpAuditingMarkDdv;
    private String accepterId;
    private Timestamp acceptDate;
    private String acceptFileId;
    private String recorderId;
    private Timestamp recordDate;
    private String auditingPersonId;
    private Timestamp auditingDate;
    private String lastModifierId;
    private Timestamp lastModifyDate;
    private String filterStatusDdv;
    private String validityDdv;

    //外联对象,单向外联。
    private DbAttachmentVO acceptFile;
    private SysUserVO accepter;
    private SysUserVO recorder;
    private SysUserVO auditingPerson;
    private SysUserVO lastModifier;


测试代码:
    private void beginPK() throws Exception {
        List<ImposedCorporationVO> vos = getVOs();
        System.out.println("开始测试,序列化后二进制数据写进本地文件...");
        System.out.println("测试记录总数:" + vos.size() + "条\n");

        System.out.print("Hessian所用时间与空间:");
        serializeByHessian(vos);

        System.out.print("\nPHPRPC所用时间与空间:");
        serializeByPhprpc(vos);
        System.out.println("\n结束测试");
    }

    private void serializeByPhprpc(List<ImposedCorporationVO> vos) throws Exception {
        String filename = "serializeByPhprpc-output";
        OutputStream os = new FileOutputStream(filename);

        PHPSerializer formator1 = new PHPSerializer();
        long startT = System.currentTimeMillis();
        for (int i = 0; i < vos.size(); i++) {
            os.write(formator1.serialize(vos.get(i)));
        }
        System.out.print((System.currentTimeMillis() - startT) + "毫秒,");
        os.close();
        File outputFile = new File(filename);
        System.out.println(outputFile.length() + "字节。");
    }

    private void serializeByHessian(List<ImposedCorporationVO> vos) throws Exception {
        String filename = "serializeByHessian-output";
        OutputStream os = new FileOutputStream(filename);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        Hessian2Output out = new Hessian2Output(bos);
        long startT = System.currentTimeMillis();
        for (int i = 0; i < vos.size(); i++) {
            out.writeObject(vos.get(i));
            os.write(bos.toByteArray());
            bos.reset();
        }
        bos.close();
        System.out.print((System.currentTimeMillis() - startT) + "毫秒,");
        os.close();
        File outputFile = new File(filename);
        System.out.println(outputFile.length() + "字节。");
    }
   发表时间:2009-03-17  
尽管我暂时没有测试数据,但是我尝试过将phprpc的序列化当成xmemcached的序列化机制,效率是比较低的,相比于spy的transcoder。我一朋友测试了phprpc和java默认的序列化机制,序列化复杂的map容器,效率也是java默认的序列化机制远远胜出。
0 请登录后投票
   发表时间:2009-03-17   最后修改:2009-03-17
反序列化我还没有测试,尚不能肯定测试结果的可靠性。

我估计造成该结果的可能原因:
1、phprpc的序列化对象数据结构描述定义不够灵活
2、phprpc不是使用二进制,而是半文本方式,所以在最终通过网络传输的数据量较大
3、我的测试代码未能让hessian2执行全部对象字段的序列化,反序列化没测试,这点有疑问。

这个结果有点离谱!
0 请登录后投票
   发表时间:2009-03-17   最后修改:2009-03-17
以上测试结果无误。

造成以上差别的主要原因是序列化后占用空间大小造成的。当序列化的对象的字段是 private 时,phprpc 会保存这个字段所属类的信息(类的全名,也就是包含包名,如果类名全名很长的话,自然会造成很大的空间差别),如果它还继承了某个类的话,继承的类的私有、保护、公开字段也会被序列化。这个设计是可以有效避免私有字段跟其父类中私有字段重名时造成冲突。hessian 中没有这个机制,它只保存字段名,而不在乎这个字段的访问权限,所以如果真的出现私有字段跟父类中的私有字段重名的话,就会有问题,当然出现这种情况的可能性也很小,另外,在对类进行设计时也可以避免这种情况发生。因此,目前来说,phprpc 序列化的数据明显大于 hessian 的数据主要是这个原因造成的,这个问题下一步也可以考虑采用跟 hessian 类似的方式,就是默认不保存私有字段的定义类名称,不过会给用户一个选项,当用户开启这个选项时,再采用目前这种方式。这样就可以保证在默认情况下,序列化的数据不会比 hessian 大出几十倍了。

另外,在这里你会发现,phprpc 序列化的数据大小是 hessian 序列化数据大小的 40 倍,而时间却只有 20 倍。另外,这里要注意有一个写文件的操作。因为写文件操作的时间明显大于内存操作(序列化)的时间。对于写入文件中数据可以认为写入的时间和写入的大小是成正比的,因此 40 倍的大小应该对应 40 倍的时间,而这里时间只有 20 倍,因此这很好的说明了 hessian 序列化的时间远远大于 phprpc 序列化的时间。
0 请登录后投票
   发表时间:2009-03-17  
dennis_zane 写道
尽管我暂时没有测试数据,但是我尝试过将phprpc的序列化当成xmemcached的序列化机制,效率是比较低的,相比于spy的transcoder。我一朋友测试了phprpc和java默认的序列化机制,序列化复杂的map容器,效率也是java默认的序列化机制远远胜出。


在 java 中复杂对象肯定是 Java 内置的序列化速度最快,因为对 map 容器这种类型的序列化方式存在本质上的实现差别。这个在我的测试中也有所体现。
0 请登录后投票
   发表时间:2009-03-17  
andot 写道
以上测试结果无误。

造成以上差别的主要原因是序列化后占用空间大小造成的。当序列化的对象的字段是 private 时,phprpc 会保存这个字段所属类的信息(类的全名,也就是包含包名,如果类名全名很长的话,自然会造成很大的空间差别),如果它还继承了某个类的话,继承的类的私有、保护、公开字段也会被序列化。这个设计是可以有效避免私有字段跟其父类中私有字段重名时造成冲突。hessian 中没有这个机制,它只保存字段名,而不在乎这个字段的访问权限,所以如果真的出现私有字段跟父类中的私有字段重名的话,就会有问题,当然出现这种情况的可能性也很小,另外,在对类进行设计时也可以避免这种情况发生。因此,目前来说,phprpc 序列化的数据明显大于 hessian 的数据主要是这个原因造成的,这个问题下一步也可以考虑采用跟 hessian 类似的方式,就是默认不保存私有字段的定义类名称,不过会给用户一个选项,当用户开启这个选项时,再采用目前这种方式。这样就可以保证在默认情况下,序列化的数据不会比 hessian 大出几十倍了。

另外,在这里你会发现,phprpc 序列化的数据大小是 hessian 序列化数据大小的 40 倍,而时间却只有 20 倍。另外,这里要注意有一个写文件的操作。因为写文件操作的时间明显大于内存操作(序列化)的时间。对于写入文件中数据可以认为写入的时间和写入的大小是成正比的,因此 40 倍的大小应该对应 40 倍的时间,而这里时间只有 20 倍,因此这很好的说明了 hessian 序列化的时间远远大于 phprpc 序列化的时间。

互联网应用中rpc传输过程序列化方案的几点思考:
1、空间,这是第一要求,基于互联网的传输,网络传输的数据量是最关键的指标,这直接关系到UI用户体验,尤其是在大负载的应用中,AMF3宁愿牺牲时间换取空间,应该是有这方面的考虑。
2、时间,序列化/反序列化的时间代价是客户端和服务器各承担一半,所以时间代价稍大是可以接受的。

就我测试结果:1万5条记录,若一次发送给客户端,phprpc的数据量是186M,Hessian才4M,40多倍的空间差距实在太大,这直接会导致B/S结构应用中网络传输数据量剧增40多倍!这是无法承受的,所以目前PHPRPC在复杂对象序列化的对象字段处理机制上必须改善,要更灵活和实用,否则,phprpc是很难在存在大数据量复杂对象传输的B/S结构中使用,而对于企业应用和稍复杂的互联网应用,此类场景是频繁出现的。

在应对大数据量复杂对象传输的场景中,phprpc需要改进。
0 请登录后投票
   发表时间:2009-03-17  
fight_bird 写道

互联网应用中rpc传输过程序列化方案的几点思考:
1、空间,这是第一要求,基于互联网的传输,网络传输的数据量是最关键的指标,这直接关系到UI用户体验,尤其是在大负载的应用中,AMF3宁愿牺牲时间换取空间,应该是有这方面的考虑。
2、时间,序列化/反序列化的时间代价是客户端和服务器各承担一半,所以时间代价稍大是可以接受的。

就我测试结果:1万5条记录,若一次发送给客户端,phprpc的数据量是186M,Hessian才4M,40多倍的空间差距实在太大,这直接会导致B/S结构应用中网络传输数据量剧增40多倍!这是无法承受的,所以目前PHPRPC在复杂对象序列化的对象字段处理机制上必须改善,要更灵活和实用,否则,phprpc是很难在存在大数据量复杂对象传输的B/S结构中使用,而对于企业应用和稍复杂的互联网应用,此类场景是频繁出现的。

在应对大数据量复杂对象传输的场景中,phprpc需要改进。


你说的很对!非常感谢您的测试,以前我也没有意识到这点。这部分我今天做一下修改,让它们在空间上不再有这样的差距,等优化之后,再请您重新测试一下,看看优化之后的效果。
0 请登录后投票
   发表时间:2009-03-17  
andot 写道
fight_bird 写道

互联网应用中rpc传输过程序列化方案的几点思考:
1、空间,这是第一要求,基于互联网的传输,网络传输的数据量是最关键的指标,这直接关系到UI用户体验,尤其是在大负载的应用中,AMF3宁愿牺牲时间换取空间,应该是有这方面的考虑。
2、时间,序列化/反序列化的时间代价是客户端和服务器各承担一半,所以时间代价稍大是可以接受的。

就我测试结果:1万5条记录,若一次发送给客户端,phprpc的数据量是186M,Hessian才4M,40多倍的空间差距实在太大,这直接会导致B/S结构应用中网络传输数据量剧增40多倍!这是无法承受的,所以目前PHPRPC在复杂对象序列化的对象字段处理机制上必须改善,要更灵活和实用,否则,phprpc是很难在存在大数据量复杂对象传输的B/S结构中使用,而对于企业应用和稍复杂的互联网应用,此类场景是频繁出现的。

在应对大数据量复杂对象传输的场景中,phprpc需要改进。


你说的很对!非常感谢您的测试,以前我也没有意识到这点。这部分我今天做一下修改,让它们在空间上不再有这样的差距,等优化之后,再请您重新测试一下,看看优化之后的效果。

百炼成钢,对国产开源的支持是一个基本义务,期待你的改进。
0 请登录后投票
   发表时间:2009-03-17  
对私有和保护字段的序列化方式优化了。

2000000次对自定义类型对象

class TestClass implements Serializable {
    private int id;
    private String userName;
    private String password;
    private int age;

    /**
     * @return the id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id the id to set
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return the userName
     */
    public String getUserName() {
        return userName;
    }

    /**
     * @param userName the userName to set
     */
    public void setUserName(String userName) {
        this.userName = userName;
    }

    /**
     * @return the password
     */
    public String getPassword() {
        return password;
    }

    /**
     * @param password the password to set
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * @return the birthday
     */
    public int getAge() {
        return age;
    }

    /**
     * @param birthday the birthday to set
     */
    public void setAge(int age) {
        this.age = age;
    }

}


                序列化|反序列化 
Java     - 时间:20735|72938 长度:137
PHPRPC   - 时间:48214|58069 长度:128
Hessian  - 时间:713818|56961 长度:97
Hessian2 - 时间:755283|54625 长度:79
0 请登录后投票
   发表时间:2009-03-17   最后修改:2009-03-17
使用svn上PHPSerializer.java新版本:611重新编译

全部记录序列化测试结果:

引用
开始测试,序列化后二进制数据写进本地文件...

测试记录总数:15072条

时间与空间:

Hessian:847毫秒,4665088字节。
PHPRPC:10615毫秒,47634761字节。

结束测试


100条记录序列化
引用
开始测试,序列化后二进制数据写进本地文件...

测试记录总数:100条

时间与空间:

Hessian:58毫秒,40758字节。
PHPRPC:187毫秒,339636字节。

结束测试


空间减少,但仍10倍于Hessian;时间上,由于存在本地文件和I/O Stream类对象初始化的耗时,不具有准确的参考价值。

在JDK自带的复杂类型方面可能仍有改进空间,如Timestamp之类。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics