• 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源代码的研究。诚哉斯言!


  • 相关阅读:
    【记录】用了七年eclipse突然改用IntelliJ IDEA------- 痛并快乐着
    【记录】集合Collection和Map整理 (看这一篇就够了)
    group by语句报with sql_mode=only_full_group_by错误
    linux下安装mysql二进制版本(5.6.34)
    linux下安装mysql二进制版本(5.7.11)
    表空间传输-例子2(full transportable)
    表空间传输-例子3(rman-transport获取传输表空间文件)
    transport_datafiles与FLASHBACK_SCN不能一起使用
    表空间传输数据文件转换
    12C添加pdb后创建用户没有相应的表空间
  • 原文地址:https://www.cnblogs.com/brucemengbm/p/6727303.html
Copyright © 2020-2023  润新知