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??墒撬o出的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源代碼的研究。誠哉斯言!
轉載于:https://www.cnblogs.com/brucemengbm/p/6727303.html
總結
以上是生活随笔為你收集整理的FlatBuffers要点的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LeetCode题解41.First M
- 下一篇: createPattern() 自定义宽