首页 > 数据库 > 数据库开发技术 > ToplingDB 的序列化框架:简介
ToplingDB 的 分布式 Compact 中 Client-Server 交互,使用了 topling-zip 中的序列化框架,该序列化框架初版完成于 2006 年,后来命名为 febird 库在 google code 上开源,再后来 google code 停止服务,febird 迁移到 github,有段时间重命名为 nark,之后重命名为 terark,目前 topling-zip 中代码的 namespace 仍是 terark。从 2006 年至今,除 namespace 名称之外,该序列化框架的接口一直保持稳定,2016 年的时候,针对 C++11 进行了模板推导相关的大幅优化,但仍保持了接口的稳定。以下为原文正文,排版有轻微改动。
原文: 最便捷、最强大、速度最快的C++序列化框架
作者: rockeet
发表日期: 2008年11月07日
分类: C++序列化
评论: 11 条
阅读次数: 6,336 次
迄今为止,我还没找到更优雅、更高效的 C++ 序列化方案,包括但不限于 boost.serialization。如果你发现了更快的,或者更易用的C++原生序列化,请告诉我……
还是用代码说明问题 (Talk is cheap. Show me the code. Torvalds, Linus (2000-08-25) ),先看这个例子:
struct MyData1 {
int a, b, c;
var_int32_t d; // d.t is int32 value var_uint64_t e; // e.t is uint64 value std::string f;
std::map<std::string, int> g;
std::set<int> h;
// 声明序列化,无版本控制,最简洁的声明,后面几个稍微复杂点 DATA_IO_LOAD_SAVE(MyData1, &a&b&c&d&e&f&g&h)};struct MyData2 {
int a, b, c;
var_int32_t d;
var_uint64_t e;
std::string f;
std::map<std::string, int> g;
std::set<int> h;
// 声明序列化,有版本控制 DATA_IO_LOAD_SAVE_V(MyData2,
1, // 当前版本 &a&b&c&d&e&f&g&h)};struct MyData3 {
int a, b, c;
boost::int32_t d;
boost::uint64_t e;
std::string f;
std::map<std::string, int> g;
std::set<int> h;
std::multiset<int> i;
unsigned version;
// 声明序列化,有版本控制 DATA_IO_LOAD_SAVE_V(MyData3,
2, // 当前版本 &a
&b
&c
&as_var_int(d) // d 声明为int32_t, 但是作为var_int32_t 来存储 &as_var_int(e) // e 声明为uint64_t, 但是作为var_uint64_t 来存储 &f
&g
&h
&vmg.since(2, i) // 版本2 新增了成员i &vmg.get_version(version) // 如果需要,将版本值存入version 成员 )};int main(int argc, char* argv[]) {
PortableDataOutput<AutoGrownMemIO> output;
PortableDataInput<MemIO> input;
output.resize(1024); // 可选,没有这一行就需要扩张几次,相当于 vector.reserve
MyData1 d1;
// set d1 values // ... MyData2 d2;
// set d2 values // ... MyData3 d3;
// set d3 values // ... output << d1 << d2 << d3; // 存储
input = output.head(); // 浅拷贝,将 input 设为 output 已写入的那部分 input >> d1 >> d2 >> d3; // 载入 //---------------------------------- // operator& 与operator<< 等效 output & d1 & d2 & d3; // 存储
input = output.head(); // 浅拷贝,将 input 设为 output 已写入的那部分
// operator& 与operator>> 等效 input & d1 & d2 & d3; // 载入}模仿这段代码,可以完成大部分的现实需求,如果有更多的需求,可以使用该框架的高级功能。例如,系统中已经定义了一些数据结构,但又不能修改现有代码,怎样给它们增加序列化能力呢?请看如下代码:
// in system header, can not changestruct SysData1 {
int a;
unsigned b;
string c;};// add these 2 function in your headertemplate<class DataIO>void DataIO_saveObject(DataIO& dio, const SysData1& x) {
dio & x.a & x.b & x.c;}template<class DataIO>void DataIO_loadObject(DataIO& dio, SysData1& x) {
dio & x.a & x.b & x.c;}// DataIO 新版中,更简单的方法
DATA_IO_LOAD_SAVE_E(SysData2, &a &b &c)// #######################################################################// 如果现存的对象需要版本控制,参考如下代码:struct SysData2 {
int a;
unsigned b;
string c;};// add these 2 function in your headertemplate<class DataIO>void DataIO_saveObject(DataIO& dio, const SysData2& x) {
const unsigned curr_version = 2;
dio & serialize_version_t(curr_version);
dio & x.a & x.b;
dio & x.c;}template<class DataIO>void DataIO_loadObject(DataIO& dio, SysData2& x) {
const unsigned curr_version = 2;
serialize_version_t loaded_version;
in >> loaded_version;
if (loaded_version.t > curr_version) {
throw BadVersionException(loaded_version.t, curr_version, className);
}
dio & x.a & x.b;
if (loaded_version.t >= 2)
dio & x.c;}// DataIO 新版中,更简单的方法:DATA_IO_LOAD_SAVE_EV(SysData2, &a &b& vmg.since(2, c))
DataIO_loadObject/DataIO_saveObject 只要在调用点可见,就可以对 SysData 进行序列化。因为 DataIO 序列化框架使用 DataIO_loadObject/DataIO_saveObject 来载入和存储对象,这样做的好处有以下几点:
在上面的代码中可以看到几个陌生的名字:MemIO, AutoGrownMemIO,PortableDataOutput, PortableDataInput…
但上面的示例代码中没有用到MinMemIO,因为 MinMemIO 没有越界检查,只有在非常简单,完全可控的情况下,才能使用它。因为没有越界检查,它的性能非常好,在大多数情况下相当于手写的 memcpy 序列化。
使用 MemIO 会稍微慢一点,但是有越界检查,读取时越界会抛出 EndOfFileException 异常,写入越界时会抛出 OutOfSpaceException 异常。
使用AutoGrownMemIO,在save时,碰到边界会自动增加内存(相当于vector.push_back自动增加内存),也可以使用resize预先分配内存(相当于vector.reserve/resize)。
这个几个MemIO类都非常简单,速度快是很自然的。
在 PortableDataOutput,PortableDataInput中,如果机器字节序是 LittleEndian,需要交换字节序,这一点,在新版的vc中和gcc中,会直接映射到一条指令:bswap。所以也不会有性能问题。
对于 var_int 的存储,无符号数,每个字节包含7个有效位,余下一位表示是否需要读取下一个字节。因此0~127仅需要一个字节,0~2^14-1需要两个字节,等等。对于有符号数,最低有效位存储符号,其余位存储绝对值。所有stl容器和string的尺寸就是用var_uint32_t存储的。
该框架中,同时实现了StreamBuffer,可以为任意Stream增加一层缓冲,往缓冲里面序列化数据的效率和MemIO系列是一样的,不同之处在于当缓冲耗尽或填满时会调用真实Stream的读写方法。这比起通常很多实现中将BufferedStream作为一个抽象,在读取哪怕一个字节时也需要一个虚函数调用,速度要快得多。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70013015/viewspace-2924696/ ,如需转载,请注明出处,否则将追究法律责任。