zoukankan      html  css  js  c++  java
  • 最便捷、最强大、速度最快的C++序列化框架

    捷、最强大、速度最快的C++序列化框架。

     

    特别注意:vc6太古老,不符合C++规范,无法使用该框架

    1.        高性能,速度非常快,比你能找到的同类产品至少快一个数量级

    2.        在网络通讯,数据库存储中非常好用。

    3.        预先支持所有基本类型,所有stl容器类型(除stack/queue之外)

    4.        支持变长int32/uint32/int64/uint64

    5.        支持stl::pair,boost::tuple

    6.        可选的版本控制,而非强制

    a)        对于小对象,通常不需要版本控制

    b)        boost::serialization的版本号是强制的,当初我设计这个序列化框架就是因为boost不能省略版本号

    7.        非侵入式设计,不污染名字空间

    8.        声明式语法,简单,可靠

    9.        …….

    该框架的易用性

    还是用代码说明问题 (Talk is cheapShow me the code. Torvalds, Linus (2000-08-25) )。

    看这个例子:

    1. struct MyData1  
    2. {  
    3.     int  a, b, c;  
    4.     var_int32_t d;  // d.t is int32 value  
    5.     var_uint64_t e; // e.t is uint64 value  
    6.     std::string f;  
    7.     std::map<std::string, int> g;  
    8.     std::set<int> h;  
    9.    
    10.     // 声明序列化,无版本控制,最简洁的声明,后面几个稍微复杂点  
    11.     DATA_IO_LOAD_SAVE(MyData1, &a&b&c&d&e&f&g&h)  
    12. };  
    13.    
    14. struct MyData2  
    15. {  
    16.     int  a, b, c;  
    17.     var_int32_t d;  
    18.     var_uint64_t e;  
    19.     std::string f;  
    20.     std::map<std::string, int> g;  
    21.     std::set<int> h;  
    22.    
    23.     // 声明序列化,有版本控制  
    24.     DATA_IO_LOAD_SAVE_V(MyData2,  
    25.         1, // 当前版本  
    26.         &a&b&c&d&e&f&g&h  
    27.         )  
    28. };  
    29.    
    30. struct MyData3  
    31. {  
    32.     int  a, b, c;  
    33.     boost::int32_t d;  
    34.     boost::uint64_t e;  
    35.     std::string f;  
    36.     std::map<std::string, int> g;  
    37.     std::set<int> h;  
    38.     std::multiset<int> i;  
    39.    
    40.     unsigned version;  
    41.    
    42.     // 声明序列化,有版本控制  
    43.     DATA_IO_LOAD_SAVE_V(MyData3,  
    44.         2, // 当前版本  
    45.         &a  
    46.         &b  
    47.         &c  
    48.         &as_var_int(d) // d 声明为int32_t, 但是作为var_int32_t 来存储  
    49.         &as_var_int(e) // e 声明为uint64_t, 但是作为var_uint64_t 来存储  
    50.         &f  
    51.         &g  
    52.         &h  
    53.         &vmg.since(2, i) // 版本2 新增了成员i  
    54.         &vmg.get_version(version) // 如果需要,将版本值存入version 成员  
    55.         )  
    56. };  
    57.    
    58. int main(int argc, char* argv[])  
    59. {  
    60.     PortableDataOutput<AutoGrownMemIO> output;  
    61.     PortableDataInput<MemIO> input;  
    62.    
    63.     output.resize(1024); // 可选,没有这一行就需要扩张几次,相当于 vector.reserve  
    64.    
    65.     MyData1 d1;  
    66.     // set d1 values  
    67.     // ...  
    68.     MyData2 d2;  
    69.     // set d2 values  
    70.     // ...  
    71.     MyData3 d3;  
    72.     // set d3 values  
    73.     // ...  
    74.     output << d1 << d2 << d3; // 存储  
    75.    
    76.     input = output.head(); // 浅拷贝,将 input 设为 output 已写入的那部分  
    77.     input >> d1 >> d2 >> d3; // 载入  
    78.    
    79. //----------------------------------  
    80.    
    81. // operator& 与operator<< 等效  
    82.     output & d1 & d2 & d3; // 存储  
    83.   
    84.     input = output.head(); // 浅拷贝,将 input 设为 output 已写入的那部分   
    85. // operator& 与operator>> 等效  
    86.     input & d1 & d2 & d3; // 载入  
    87. }  
    88.    

    模仿这段代码,可以完成大部分的现实需求,如果有更多的需求,可以使用该框架的高级功能。例如,系统中已经定义了一些数据结构,但又不能修改现有代码,怎样给它们增加序列化能力呢?请看如下代码:

    1. // in system header, can not change  
    2. struct SysData1 {  
    3.     int a;  
    4.     unsigned b;  
    5.     string c;  
    6. };  
    7. // add these 2 function in your header  
    8. template<class DataIO>  
    9. void DataIO_saveObject(DataIO& dio, const SysData1& x) {  
    10.     dio & x.a & x.b & x.c;  
    11. }  
    12. template<class DataIO>  
    13. void DataIO_loadObject(DataIO& dio, SysData1& x) {  
    14.     dio & x.a & x.b & x.c;  
    15. }  
    16. // DataIO 新版中,更简单的方法    
    17. DATA_IO_LOAD_SAVE_E(SysData2, &a &b &c)  
    18. // #######################################################################  
    19. // 如果现存的对象需要版本控制,参考如下代码:  
    20. struct SysData2 {  
    21.     int a;  
    22.     unsigned b;  
    23.     string c;  
    24. };  
    25. // add these 2 function in your header  
    26. template<class DataIO>  
    27. void DataIO_saveObject(DataIO& dio, const SysData2& x) {  
    28.     const unsigned curr_version = 2;  
    29.     dio & serialize_version_t(curr_version);  
    30.     dio & x.a & x.b;  
    31.     dio & x.c;  
    32. }  
    33. template<class DataIO>  
    34. void DataIO_loadObject(DataIO& dio, SysData2& x) {  
    35.     const unsigned curr_version = 2;  
    36.     serialize_version_t loaded_version;  
    37.     in >> loaded_version;  
    38.     if (loaded_version.t > curr_version) {  
    39.         throw BadVersionException(loaded_version.t, curr_version, className);  
    40.     }  
    41.     dio & x.a & x.b;  
    42.     if (loaded_version.t >= 2)  
    43.         dio & x.c;  
    44. }  
    45. // DataIO 新版中,更简单的方法:  
    46. DATA_IO_LOAD_SAVE_EV(SysData2, &a &b& vmg.since(2, c))  

    How It Works

    DataIO_loadObject/DataIO_saveObject只要在调用点可见,就可以对 SysData 进行序列化。因为 DataIO 序列化框架使用DataIO_loadObject/DataIO_saveObject来载入和存储对象,这样做的好处有以下几点:

    l 非侵入,对象类型和加载/存储函数可以分离定义

    n  否则无法为不可更改代码的对象增加序列化能力

    l 这两个函数可以定义在任何名字空间

    n  根据C++的名字查找规则,只要在调用环境和每个参数所在的名字空间中有相应的匹配函数,就会使用该函数。我们需要有效地利用这一点。

    l DataIO_loadObject/DataIO_saveObject这两个函数名较长,并且罕见

    n  因此不会与系统中的其他标识符发生冲突。(对比boost::serialization中的serialize函数,它就比较容易和其他名字发生冲突,serialize太常见了)。

     

    性能

    在上面的代码中可以看到几个陌生的名字:MemIO, AutoGrownMemIO,PortableDataOutput, PortableDataInput…

     但上面的示例代码中没有用到MinMemIO,因为 MinMemIO 没有越界检查,只有在非常简单,完全可控的情况下,才能使用它。因为没有越界检查,它的性能非常好,在大多数情况下相当于手写的 memcpy 序列化。

    使用 MemIO 会稍微慢一点,但是有越界检查,读取时越界会抛出 EndOfFileException 异常,写入越界时会抛出 OutOfSpaceException 异常。

    使用AutoGrownMemIO,在save时,碰到边界会自动增加内存(相当于vector.push_back自动增加内存),也可以使用resize预先分配内存(相当于vector.reserve/resize)。

    这个几个MemIO类都非常简单,速度快是很自然的。

    PortableDataOutputPortableDataInput中,如果机器字节序是LittleEndian,需要交换字节序,这一点,在新版的vc中和gcc中,会直接映射到一条指令:bswap。所以也不会有性能问题。

    对于var_int的存储,无符号数,每个字节包含7个有效位,余下一位表示是否需要读取下一个字节。因此0~127仅需要一个字节,0~2^14-1需要两个字节,等等。对于有符号数,最低有效位存储符号,其余位存储绝对值。所有stl容器和string的尺寸就是用var_uint32_t存储的。

    该框架中,同时实现了StreamBuffer,可以为任意Stream增加一层缓冲,往缓冲里面序列化数据的效率和MemIO系列是一样的,不同之处在于当缓冲耗尽或填满时会调用真实Stream的读写方法。这比起通常很多实现中将BufferedStream作为一个抽象,在读取哪怕一个字节时也需要一个虚函数调用,速度要快得多。

    扩展应用

    使用该序列化框架,我实现了一个不需要 IDL的 RPC 。

    使用该序列化框架,对 Berkeley DB 进行包装,可以让它象标准容器一样使用,免除了复杂的编码。后面我会继续介绍。

    项目地址:http://code.google.com/p/febird

  • 相关阅读:
    GATK-BWA-MEM handle GRCh38 alternate contig mappings
    GATK使用说明-GRCh38(Genome Reference Consortium)(二)
    GATK使用说明(一)
    [python] 线程池
    [python] 线程锁
    [python] 线程简介
    [linux] 更改目录显示颜色
    限制登录次数
    项目经验总结-twice
    Java泛型底层源码解析--ConcurrentHashMap(JDK1.6/JDK1.7/JDK1.8)
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318628.html
Copyright © 2011-2022 走看看