FlatBuffers要点
FlatBuffers发布出来一周多,周末便抽时间先研究下它的使用方法。Flatbuffers的idl的语法主要参考[http://google.github.io/flatbuffers/md__schemas.html ]。本文主要介绍几个它的monster.fbs没有给出说明的几个语法点和相关的注意事项。
1 comment
它的注释中介绍了”///",说明是可以生成document comment. 我写了如下fbs代码(假设文件名称还是monster.fbs):
/// struct Vec3 {
/// x:float;
/// y:float;
/// z:float;
/// }
"flatc -c monster.fbs"命令编译后,生成代码为:
/// struct Vec3 { x:float; y:float; z:float; }
2 struct
struct默认的aliagnment是4Byte.
如下fbs代码(假设文件名称还是monster.fbs):
struct Vec4 {
x : float;
y : short;
z : float;
w : short;
}
"flatc -c monster.fbs"命令编译后,生成代码为:
MANUALLY_ALIGNED_STRUCT(4) Vec4 {
private:
float x_;
int16_t y_;
int16_t __padding0;
float z_;
int16_t w_;
int16_t __padding1;
public:
Vec4(float x, int16_t y, float z, int16_t w)
: x_(flatbuffers::EndianScalar(x)), y_(flatbuffers::EndianScalar(y)), __padding0(0), z_(flatbuffers::EndianScalar(z)), w_(flatbuffers::EndianScalar(w)), __padding1(0) {}
float x() const { return flatbuffers::EndianScalar(x_); }
int16_t y() const { return flatbuffers::EndianScalar(y_); }
float z() const { return flatbuffers::EndianScalar(z_); }
int16_t w() const { return flatbuffers::EndianScalar(w_); }
};
STRUCT_END(Vec4, 16);
3 original_order
FB(FlatBuffers的简称,下同)用original_order来保持fbs中table的field顺序,FB说明中说明它是用来修饰table的(”original_order (on a table)"),其实他也可以修饰struct。
如下fbs代码(假设文件名称还是monster.fbs):
struct Vec4_ (original_order) {
x : float;
y : short;
z : float;
w : short;
}
"flatc -c monster.fbs"命令编译后,生成代码为:
STRUCT_END(Vec4, 16);
MANUALLY_ALIGNED_STRUCT(4) Vec4_ {
private:
float x_;
int16_t y_;
int16_t __padding0;
float z_;
int16_t w_;
int16_t __padding1;
public:
Vec4_(float x, int16_t y, float z, int16_t w)
: x_(flatbuffers::EndianScalar(x)), y_(flatbuffers::EndianScalar(y)), __padding0(0), z_(flatbuffers::EndianScalar(z)), w_(flatbuffers::EndianScalar(w)), __padding1(0) {}
float x() const { return flatbuffers::EndianScalar(x_); }
int16_t y() const { return flatbuffers::EndianScalar(y_); }
float z() const { return flatbuffers::EndianScalar(z_); }
int16_t w() const { return flatbuffers::EndianScalar(w_); }
};
STRUCT_END(Vec4_, 16);
4 force_align
force_align这个关键字用来对struct进行align,此处我想说明FB的一个bug。
如下fbs代码(假设文件名称还是monster.fbs):
struct Vec3 (force_align : 8 ) {
x : float;
y : float;
z : float;
}
"flatc -c monster.fbs"命令编译后,生成代码为:
MANUALLY_ALIGNED_STRUCT(8) Vec3 {
private:
float x_;
float y_;
float z_;
public:
Vec3(float x, float y, float z)
: x_(flatbuffers::EndianScalar(x)), y_(flatbuffers::EndianScalar(y)), z_(flatbuffers::EndianScalar(z)) {}
float x() const { return flatbuffers::EndianScalar(x_); }
float y() const { return flatbuffers::EndianScalar(y_); }
float z() const { return flatbuffers::EndianScalar(z_); }
};
STRUCT_END(Vec3, 12);
请注意上面最后一行代码的最后一个参数"12”,我已经说明以8Byte作为alignment,但是它给出的struct Vec3的size仍然为12,如果你使用上面的代码,g++会给出这样的错误提示"error: static assertion failed: compiler breaks packing rules”。
解决方法有三个,第一,你把这行代码注释掉。or 第二,把数字“12”手工改成“16”。or 第三,等待官方修正这个bug(我已经提交了这个bug:https://github.com/google/flatbuffers/issues/18)。
补充:bug已经由gwvo(https://github.com/gwvo)修正了。修正链接见(https://github.com/AlexStocks/flatbuffers/commit/65cfa18855abc712faa1bf0cb5c3b88ab8df4b28)。
之所以Vec3的size没有计算正确,原因是作者分析完struct的各个成员后忘了这个语法特性了。我下面稍微补充下FB的编译器出现这个bug的原因。
bug修改之前,void Parser::ParseDecl() 一块代码如下:
// 验证struct的开头为 "{"
Expect('{');
// 分析struct的每一行(field),其主功能是为struct添加member并增加struct的bytesize
while (token_ != '}') ParseField(struct_def);
// 为struct添加padding成员以进行alignment,然后计算struct的bytesize值
// 注意:作者忘了"force_align"这个关键语法特性就计算struct的bytesize
struct_def.PadLastField(struct_def.minalign);
// 验证struct的结尾为 "}"
Expect('}');
// 此时才开始查找"force_align"这个keyword
auto force_align = struct_def.attributes.Lookup("force_align");
if (fixed && force_align) {
auto align = static_cast<size_t>(atoi(force_align->constant.c_str()));
// 虽然计算出了struct应该以align为基准进行alignment,但是为时已晚,无法改变struct的bytesize了
struct_def.minalign = align;
}
通过上面分析,就可以晓得问题所在,修正后的代码为:
Expect('{');
while (token_ != '}') ParseField(struct_def);
auto force_align = struct_def.attributes.Lookup("force_align");
if (fixed && force_align) {
auto align = static_cast<size_t>(atoi(force_align->constant.c_str()));
struct_def.minalign = align;
}
struct_def.PadLastField(struct_def.minalign); // !!!
Expect('}'); // !!!
}
注意上面的代码块,仅仅是对"!!!"标识的两行代码在代码块中往下移动了几行,就可以正确计算struct的bytesize了。再次感谢gwvo的工作。
5 struct&table
table 是FB实现向前\向后兼容的关键。table每个field默认都是optional的,而struct的每个成员是required的。把idl转换为C++ or Java语言时候,FB不会修改struct成员的顺序(如果struct的成员无法alignment FB会自动添加padding成员),但是会改变table成员的顺序以使得它占用最小的内存空间。所以,table 提供向前\向后兼容特性,而你一旦定义一个struct后,你就无法再添加新的member。
table的每个对象被序列化后的内存空间中都存在着一个额外(除却数据内存空间外)的辅助空间以说明其内存对象分布,称之为vtable。通过vtable我们可以知道这个table对象有哪些成员。如果同一个table的不同对象被序列化进同一个内存空间内,那么只有第一个对象存储了vtable的详细数据,而其他对象的vtable区域只存储一个指向第一个对象的vtable的指针即可。因为table对象需要vtable以说明自己,所以对他它序列化后占用的内存空间一般都比序列化一个struct对象后占用的内存空间大。
vtable的每个元素占用2Bytes。它的第一个element是vtable的elements总数目(包含第一个element自身),第二个是对象的以字节为单位的内存大小(包含vtable占用的内存空间大小),后面的element就是table各个成员在object区域的伪指针(即偏移大小)。如果table有N个member,则vtable大小就是((N + 2) * 2) Bytes.
FB的对象区域每个成员基本上都是len+value形式。vtable内则是type id + offset形式。offset可以理解为一个伪指针,通过它可以找到每个成员。FB对每个成员进行赋值之前,要先进性alignment,然后把value转换为endian形式,FB保证这些值在不同平台上都有效。
反序列化一个对象后,访问一个对象的某个成员时,如果其序列化后的空间内没有这个成员的数据,那么就返回其默认值。同理,序列化一个对象时,如果这个对象的成员的值与默认值相等,则不会把这个值序列化进内场空间。如果一个table的field都用默认值,那么此时对它序列化后占用的内存空间要比序列化一个struct对象后占用的内存空间小。
FB还有一个类型union,它的文档里面提到"FlatBuffers can of course be wrapped inside other containers where needed, or you can use its union feature to dynamically identify multiple possible sub-objects stored. Additionally, it can be used together with the schema parser if full reflective capabilities are desired.“。我理解不多,有待后面分析。
6 summary
FB的document里面提到开发它的缘由。
“在过去的好日子里,说到提高性能就是开发更高效的CPU指令和提供更短的CPU计算周期,但现在那个好日子一去不复返了,因为回首回望,我们发现CPU已经发展太快而把他的小弟内存拉下了好几圈了。现在提高性能的战场应该是在内存而非CPU了。快速高效的方法应该是:一个对象在内存中如何高效的占用尽可能少的空间,如何更快速的访问它,以及怎么为它分配这些空间和如何拷贝它。”
“进程一个重要的是任务就是对它的数据进行序列化,这可能要使用很多临时的变量以分析和暂时存储这些数据,也可能使用不甚高效的内存分配策略。而FB可以做到不使用临时对象、没有额外的内存分配、不拷贝和让对象占用尽可能少的内存空间。除了做到这些,FB能保证数据本身的向前\向后兼容性、跨平台特性。另外,它以小端数据格式作为标准数据格式”
“FB主要关注移动开发平台(这个平台与PC平台相比就如同上世界把PC与大型机相比一样,它的内存空间大小和数据传输速度都稍显紧缺),它尤其关注一类移动应用:games”
根据我对FB源码的研究,诚哉斯言!
FlatBuffers要点的更多相关文章
- C++常见笔试面试要点以及常见问题
1. C++常见笔试面试要点: C++语言相关: (1) 虚函数(多态)的内部实现 (2) 智能指针用过哪些?shared_ptr和unique_ptr用的时候需要注意什么?shared_ptr的实现 ...
- 《高性能javascript》一书要点和延伸(上)
前些天收到了HTML5中国送来的<高性能javascript>一书,便打算将其做为假期消遣,顺便也写篇文章记录下书中一些要点. 个人觉得本书很值得中低级别的前端朋友阅读,会有很多意想不到的 ...
- [php]laravel框架容器管理的一些要点
本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. ...
- 基础笔记(一):C#编程要点
前言 来源于手中日常摘录的资料和书籍,算是对看过的东西的总结,部分注有阅读心得,也有部分只提出大纲或结论.(备注:本篇文章中大部分要点需要有实际的开发经验,有助于阅读理解.) 目录 const ...
- CORS基础要点:关于dataType、contentType、withCredentials
事实上,面试时我喜欢问跨域,因为多数开发者都知道它并且常用,而我希望能从面试者的回答中知道他在这个问题的深入程度,进一步看看面试者研究问题的思维方式及钻研精神,然而确实难到了很多人,当然这也不是面试通 ...
- 漫谈C++:良好的编程习惯与编程要点
以良好的方式编写C++ class 假设现在我们要实现一个复数类complex,在类的实现过程中探索良好的编程习惯. ① Header(头文件)中的防卫式声明 complex.h: # ifndef ...
- 推荐Linux管理员不可不知十大PHP安全要点 - SCutePHP
PHP是使用最广泛的脚本编程语言之一.市场份额颇能说明其主导地位.PHP 7已推出,这个事实让这种编程语言对当前的开发人员来说更具吸引力.尽管出现了一些变化,但是许多开发人员对PHP的未来持怀疑态度. ...
- PHPCMS与UCenter整合要点
要点一: PHPCMS不能直接与UCenter整合,而是要经过 PHPSSO 适配,因此应用主URL应是 http://phpcms_url/phpsso_server 这种模式的. 要点二: 因为 ...
- EDM制作要点
EDM是Email Direct Marketing的缩写,虽然也是html,但是和我们在网页上使用的html不同,因为安全原因,各大邮箱服务商级邮件客户端都会对邮件内容进行一定程度上的处理,不会按照 ...
随机推荐
- Ext.grid.plugin.RowExpander的简单用法
有时候,我们在grid里渲染数据时,由于某些字段的内容太长,而grid又不会自动出现滚动条,于是溢出的内容后面就会出现省略号, 导致信息展示不完全.如果,这个信息不太重要,展示不完全也无关紧要.可是, ...
- bootrom启动流程【转】
转自:http://blog.csdn.net/blueoceanindream/article/details/6851787 闲来无事,总结一下linux bootrom的启动流程: 环境:MIP ...
- 新环境配置与使用Vim指南
1.下载源码 git clone git@github.com:vim/vim.git 2.编译 1.安装依赖软件 sudo apt-get install libncurses5-dev libgn ...
- 没有显示器且IP未知的情况下登录树莓派
如果是没有显示器操作树莓派,可能会不知道树莓派有线网卡自动分配到的IP地址,不知道登录到哪儿.以下提供详细操作步骤解决这个问题. 网段扫描法这个是推荐的办法.网段扫描工具很多,推荐一个Advanced ...
- Tomcat遇到”Error listenerStart”或”Error filterStart”问题且无详细日志时的log配置.
昨天部署web应用到Tomcat之后,无法成功启动,并且控制台没有详细的错误信息,顶多就两行提示信息,例如:严重: Error listenerStart严重: Context [/lizongbo] ...
- jQuery 清除div内容
$.ajax({ url: "SearchSN.aspx", data: "SN=" + $("#txtS ...
- bzoj4127
肯定是树链剖分+线段树,关键是怎么维护 绝对值和这个东西显然不能简单的合并标记 因为对于负数,加之后绝对值和是变小的 那我们考虑对负数和非负数数分别维护 下面的问题就是经过操作如果负数变成了正数怎么办 ...
- 快速掌握 Android Studio 中 Gradle 的使用方法 [转http://blog.csdn.net/feelang/article/details/41783317]
Gradle是可以用于Android开发的新一代的 Build System, 也是 Android Studio默认的build工具. Gradle脚本是基于一种JVM语言 -- Groovy,再加 ...
- UVa 1609 (博弈) Foul Play
姑且把它归类为一道博弈吧,毕竟这也是在找必胜方案. 十分有意思的一道题目,设计一种方案让你支持的1队获胜. 题目给出了两个很重要的条件: 1队能打败至少一半的队伍 对于1队不能打败的黑队,一定存在一个 ...
- java程序员修炼之道
今天在论坛里看到了一位工作10年的java大牛总结的java程序员修炼之道,看完后给出的评价是:字字玑珠,深入人心,猛回头,自己一无是处··· 大牛告诉我们应该好好学习与修炼以下知识与技能 Java语 ...