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这个keyword用来对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源代码的研究。诚哉斯言!

时间: 2024-10-12 02:54:26

FlatBuffers要点的相关文章

javascript必须知道的知识要点(二)

该文章不详细叙述各知识要点的具体内容,仅把要点列出来,供大家学习的时候参照,或者检测自己是否熟练掌握了javascript,清楚各个部分的内容. 内建对象可划分为数据封装类对象.工具类对象.错误类对象,如下. 数据封装类对象: Number对象 String对象 Boolean对象 Array对象 Object Function 工具类对象: Math对象 Date对象 RegExp对象 错误类对象: Error对象  Number对象 JavaScript 只有一种数字类型.Number,包括

ZooKeeper架构设计及其应用要点

ZooKeeper是一个开源的分布式服务框架,它是Apache Hadoop项目的一个子项目,主要用来解决分布式应用场景中存在的一些问题,如:统一命名服务.状态同步服务.集群管理.分布式应用配置管理等,它支持Standalone模式和分布式模式,在分布式模式下,能够为分布式应用提供高性能和可靠地协调服务,而且使用ZooKeeper可以大大简化分布式协调服务的实现,为开发分布式应用极大地降低了成本. 总体架构 ZooKeeper分布式协调服务框架的总体架构,如图所示: ZooKeeper集群由一组

XHTML学习要点

目标 掌握XHTML语法,能正确书写出符合规则的文档. 要点 基本概念,与HTML有什么不一样 基本语法规则: XHTML 文档必须拥有一个根元素 标签名.属性名称必须小写 属性值必须加引号 属性不能简写 !DOCTYPE不可缺少 不允许空标签,<hr> , <br> 和 <img>应被替换为<hr />, <br /> 和 <img />. 除!DOCTYPE外,其他标签必须要被关闭. 非空标签必须使用结束标签. 空标签也必须被关闭

如何编写更棒的代码:11个核心要点

作为一个合格的程序员,有太多的理由促使你去编写干净利落且可读性强的代码.最重要的是因为你编写的代码,将来会有很多人一次次地阅读.当你有一天回过头来看自己的代码时,你就会明白编写优雅的代码是多么的重要.另外,如果别人来阅读你编写的代码,你是否想知道别人看到那些烂代码无比抓狂的感受.因此,花多一点的时间去编写优雅的代码,将来说不定会给你节省更多的时间. 那么,如何编写更棒的代码,下面是11条基本规则: 1.保持方法简短扼要 2.永远永远不要将同一个变量用于不同的目的 3.尽可能让变量和方法的名称能够

7月10日到7月15日技术积累要点

总结过去一周技术积累要点: Environment.NewLine(换行): string.Concat()的时间复杂度低于+=: 单元测试的规则: 创建日志的规则(LoggerFactory.CreateLog): IDE工具调试改值的技巧升级: Json序列化和反序列化使用升级: Mapper.CreateMap()的使用升级: string.Split()的使用升级: float.TryParse()的使用升级: float.Parse()的使用升级: WCF服务的使用开发流程: 调试技巧

H3C子接口配置要点及实例说明

 类型一:以太网子接口配置要点(单臂路由) 第一步:在路由器对端的交换机上配置好vlan信息(如vlan10/vlan20) 第二步:将交换机上与路由器直接相连的以太口配置成trunk口并同意全部vlan信息通过. 第三步:进入路由器以太口的子接口如ethernet0/0/0.10和ethernet0/0/0.20 第四步:配置好该子接口的ip地址,一般配置成相应vlan网关ip地址 第五步:为该子接口封装802.1q协议并指明该子接口相应vlan的vid 比如:[Huawei-Ethern

游戏UI设计要点——你抓住玩家的心了吗?

游戏美术行情在线        游戏UI设计的好坏决定了你和玩家之间的关系,到底是初次相遇的美好呢还是"山无棱天地合乃敢与君绝"?       那么要做一名具有出色"撩妹"技巧的UI设计师,就要抓住游戏UI设计的要点,这样才能抓住玩家的心嘛!今天就跟大家聊聊游戏UI的撩妹哦不设计要点. 话说"士为知己者死,女为悦己者容",那么如何才能成功地引起对方的注意呢?这就必须有个互动的过程啦,这个过程通俗点说的呢就是"撩妹".     

深度学习:从头设计一个TensorFlow3一样的新一代深度学习系统,到底需要把握哪些要点?

深度学习工具潮流滚滚,各种工具层出不穷.也有各种文章从易用性,可移植性,灵活性和效率方面对于各个系统进行比较.这篇文章希望从系统设计上面来讲来回答这个讨论这个问题:如果想到从头设计一个TensorFlow3一样的新一代深度学习系统,到底需要把握哪些要点. 计算单元:从layer abstraction到operator 大家熟悉的第一代深度学习系统,以cuda-convnet21和caffe为代表.这些系统主要的一大特点是提出了一个以深度学习计算层次layer为基本单元的计算单位.不同的laye

【netty】Netty系列之Netty百万级推送服务设计要点

1. 背景 1.1. 话题来源 最近很多从事移动互联网和物联网开发的同学给我发邮件或者微博私信我,咨询推送服务相关的问题.问题五花八门,在帮助大家答疑解惑的过程中,我也对问题进行了总结,大概可以归纳为如下几类: Netty是否可以做推送服务器? 如果使用Netty开发推送服务,一个服务器最多可以支撑多少个客户端? 使用Netty开发推送服务遇到的各种技术问题. 由于咨询者众多,关注点也比较集中,我希望通过本文的案例分析和对推送服务设计要点的总结,帮助大家在实际工作中少走弯路. 1.2. 推送服务