感謝Atry的指導,筆者修改了一下範例,以求更客觀的比較結果,順便也比較一下修改前及修改後的效能差異
主要修改的有兩部分:
1. 使用optional取代required
2. scalar最佳化
proto文件如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
message BigData {
repeated MediumData mediumDatas = 1;
}
message MediumData {
repeated SmallData smallDatas = 1;
}
message SmallData {
optional sfixed32 intValue = 1;
optional fixed32 uintValue = 2;
optional bool booleanValue = 3;
optional float numberField = 4;
}
|
proto文件記載了BigData、MediumData及SmallData,SmallData只有基本資料型別,而MediumData及BigData則是用巢狀方式讓資料格式更大,repeated屬性可以讓欄位變成陣列(Array)或清單(List),由於直接使用ProtocolBuffer轉出來的類別轉AMF3的話,AMF3會把繼承的Message類一起寫入,為了避免這點,範例再寫一組同樣格式但沒有繼承的資料物件給AMF3使用,以求更客觀的比較結果
範例為測試AMF3及ProtocolBuffer的序列化及反序列化速度,結果如下
以大小來看,Protocol Buffer比較小,即使是巢狀資料也比AMF小一點,但速度方面是AMF3較好,原因可能是AMF3的序列化及反序列化為底層API,效能可能有被優化,不過以實用上來看Protocl Buffer的速度是可接受的,畢竟平常的封包也不會太大。
再來比較一下proto文件的required及optional差異,required是指示欄位一定要有值,而optional是可有可無,以官網的說法 ,required欄位如果之後要改成optional會有讀取問題,建議是使用optional,為了試驗,筆者把上述的smallData所有欄位改成required,比較一下效能,結果如下
如果是使用required,Protocol buffer的變化不大,筆者之前有試過optional加default的話,會比required欄位小很多,但如果都使用賦值,差異就看不出來了
另一個要測的是scalar,也就是欄位宣告的類型,Protocol Buffer跟AMF不同,即使類型同樣為正負號整數,必須依使用情況宣告成不同的scalar才能發揮Protocol Buffer最大功效,可參考http://code.google.com/apis/protocolbuffers/docs/proto.html#scalar
也就是當使用整數時,如果是小值,可宣告成int,如果值常超過228,要用fixed32,或是整數常出現負號,應使用sint32,針對不同需求應宣告成對應類型,這部分就要看實例時,欄位要怎麼設計,筆者把範例修改成只宣告uint及int,比較一下兩者的差異
出乎意料的是差異不大,只有大小有明顯的變化,或許是筆者範例的欄位太少,只有一點的差異讓我有點意外。
最後做個總結,Protocol Buffer的測試似乎依實例來測會比較準,筆者目前寫的測試方法還是無法比較出差異,主要應該是值都是重覆使用,無法發揮封包特性,或許還要再想一個比較好的實例來測會比較準
如果有網友測出不同結果,或覺得範例測試方法有誤,歡迎留言討論。
測試環境為: ProtocolBuffer 2.4.0a、Flex 4.0 SDK、Protoc-gen-as3 0.9.0、FlashPlayer 10.3 Release
相關文章: [教學]在Flash中使用Google Protocol Buffers
轉載請註明出處
测试用例全错!!!!!
1. required应慎用。参见 http://code.google.com/apis/protocolbuffers/docs/proto.html 中的“Required Is Forever”一段。试试把proto文件中的required改成optional再测测性能,有惊喜。
2. default选项应该同optional一起用而不是和required一起用。参见 http://code.google.com/apis/protocolbuffers/docs/proto.html#optional 。
3. 重复出现相同的长字符串是一种实践中根本不可能出现的情况,偏偏AMF对这种情况有优化,这不公平。考虑AMF3一个small包就有157B,包含了500个small的medium包竟然才14.8KB,还不到small的一百倍。
4. uint32类型适合出现小数字几率高的情形。0xFFFFFF用uint32类型存取很低效,应该用fixed32。参见 http://code.google.com/apis/protocolbuffers/docs/proto.html#scalar
5. int32类型适合出现小正数几率高的情形。2147483647用int32类型存取很低效,应该用sfixed32。
总之,这样的测试应该尽量用接近实践中的数据。实践中应该有一定比例采用默认值而不设置值的。随机生成的数据也要考虑实践中的分布情况。实践中除了某些UUID外,别的数据很少是均匀分布在0到0xFFFFFFFF之间的而是集中在小数字的情形上。
实践中最常见的数据包往往内容不大,最多上百字节,但格式异常复杂,嵌套层次很多,此外还存在大量小整数。这类数据正好是Protobuf专门优化的类型。
Atry
2011年5月26日下午9:11此外,字符串和字节数组的字段可以去掉。大的字符串和字节数组不论用何种格式都是简单的字节拷贝,不会有太大的性能差异。
Atry
2011年5月26日下午9:13还有,全部测试数据都是些重复数据,对这些重复数据测试压缩率,当然会超级高了,说明不了任何问题。
Atry
2011年5月26日下午9:23Atry感謝您的回應,很高興能有機會跟您討論,這次的範例在測試上確實沒考慮到實際的使用情形,數值及欄位的設計的確有誤,我會參考您的意見再改寫一版測試,也希望您能繼續給予批評與指教,謝謝
GD
2011年5月26日下午9:55你的测试数据用的是Debugger版本的Flash Player,对解释执行的protobuf很不公平。因为Protobuf的序列化/反序列化代码是在解释器中执行的,性能受Flash Player版本影响很大;而AMF则是调用了C++库,性能与Flash Player版本关系不大。
我自己用最新的Flash Player 10.3非Debugger版测试,Protobuf并不比AMF慢很多,二者性能在同一数量级。
Atry
2011年6月2日下午5:08此外,您的测试仍然没有体现optional字段留空不填的情形,也没有测试小整数(RIA应用中最常见的数据)的性能。
Atry
2011年6月2日下午5:10抛开optional字段留空以及小整数不论,目前您的测试代码如果在非debugger的Flash Player上跑,性能也不会有这么大差距。这是我跑出来的结果:
[PROTOCOL BUFFERS]
serialization
small:
size = 18b, cost = 0ms
medium:
size = 9.77kb, cost = 1ms
big:
size = 97.7kb, cost = 12ms
deserialization
small:
cost = 0ms
medium:
cost = 1ms
big:
cost = 11ms
[AMF3]
serialization
small:
size = 90b, cost = 0ms
medium:
size = 12.8kb, cost = 0ms
big:
size = 127kb, cost = 10ms
deserialization
small:
cost = 0ms
medium:
cost = 1ms
big:
cost = 7ms
Total Memory = 4.41mb
Atry
2011年6月2日下午5:15有个老外也做过AMF和protobuf的性能比较,他用的是较早的protoc-gen-as3,性能比 0.9.x 稍差一点。
http://backgroundthinking.wordpress.com/2010/05/24/performance-of-google%E2%80%99s-protocol-buffers-in-flex-redux/
这个老外没贴代码,不知道他用的什么数据,但估计他填的数据中有许多小整数。在小整数很多的情况下,protobuf格式比AMF会小得多。他的测试结果可以体现这一点。
Atry
2011年6月2日下午5:23測試的部分還要再設計,這次測試主要是測大數據,且沒有測平均數,改良的做法或許可以參考你貼的網站
GD
2011年6月2日下午10:47但不管怎样,debugger版的Flash Player会大大降低ActionScript执行效率。用debugger版测出来的数据没有参考价值。
Atry
2011年6月2日下午10:50嗯,您說的沒錯,會造成差異的因素應該盡量避免,我已更新release版的結果上去
GD
2011年6月3日上午1:05