日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

probuf了解

發(fā)布時(shí)間:2025/3/20 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 probuf了解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

人們一直在強(qiáng)調(diào),同 XML 相比, Protobuf 的主要優(yōu)點(diǎn)在于性能高。它以高效的二進(jìn)制方式存儲(chǔ),比 XML 小 3 到 10 倍,快 20 到 100 倍。

對(duì)于這些 “小 3 到 10 倍”,“快 20 到 100 倍”的說法,嚴(yán)肅的程序員需要一個(gè)解釋。因此在本文的最后,讓我們稍微深入 Protobuf 的內(nèi)部實(shí)現(xiàn)吧。

有兩項(xiàng)技術(shù)保證了采用 Protobuf 的程序能獲得相對(duì)于 XML 極大的性能提高。

第一點(diǎn),我們可以考察 Protobuf 序列化后的信息內(nèi)容。您可以看到 Protocol Buffer 信息的表示非常緊湊,這意味著消息的體積減少,自然需要更少的資源。比如網(wǎng)絡(luò)上傳輸?shù)淖止?jié)數(shù)更少,需要的 IO 更少等,從而提高性能。

第二點(diǎn)我們需要理解 Protobuf 封解包的大致過程,從而理解為什么會(huì)比 XML 快很多。

Google Protocol Buffer 的 Encoding

Protobuf 序列化后所生成的二進(jìn)制消息非常緊湊,這得益于 Protobuf 采用的非常巧妙的 Encoding 方法。

考察消息結(jié)構(gòu)之前,讓我首先要介紹一個(gè)叫做 Varint 的術(shù)語。

Varint 是一種緊湊的表示數(shù)字的方法。它用一個(gè)或多個(gè)字節(jié)來表示一個(gè)數(shù)字,值越小的數(shù)字使用越少的字節(jié)數(shù)。這能減少用來表示數(shù)字的字節(jié)數(shù)。

比 如對(duì)于 int32 類型的數(shù)字,一般需要 4 個(gè) byte 來表示。但是采用 Varint,對(duì)于很小的 int32 類型的數(shù)字,則可以用 1 個(gè) byte 來表示。當(dāng)然凡事都有好的也有不好的一面,采用 Varint 表示法,大的數(shù)字則需要 5 個(gè) byte 來表示。從統(tǒng)計(jì)的角度來說,一般不會(huì)所有的消息中的數(shù)字都是大數(shù),因此大多數(shù)情況下,采用 Varint 后,可以用更少的字節(jié)數(shù)來表示數(shù)字信息。下面就詳細(xì)介紹一下 Varint。

Varint 中的每個(gè) byte 的最高位 bit 有特殊的含義,如果該位為 1,表示后續(xù)的 byte 也是該數(shù)字的一部分,如果該位為 0,則結(jié)束。其他的 7 個(gè) bit 都用來表示數(shù)字。因此小于 128 的數(shù)字都可以用一個(gè) byte 表示。大于 128 的數(shù)字,比如 300,會(huì)用兩個(gè)字節(jié)來表示:1010 1100 0000 0010

下圖演示了 Google Protocol Buffer 如何解析兩個(gè) bytes。注意到最終計(jì)算前將兩個(gè) byte 的位置相互交換過一次,這是因?yàn)?Google Protocol Buffer 字節(jié)序采用 little-endian 的方式。

圖 6. Varint 編碼

消息經(jīng)過序列化后會(huì)成為一個(gè)二進(jìn)制數(shù)據(jù)流,該流中的數(shù)據(jù)為一系列的 Key-Value 對(duì)。如下圖所示:

圖 7. Message Buffer

采用這種 Key-Pair 結(jié)構(gòu)無需使用分隔符來分割不同的 Field。對(duì)于可選的 Field,如果消息中不存在該 field,那么在最終的 Message Buffer 中就沒有該 field,這些特性都有助于節(jié)約消息本身的大小。

以代碼清單 1 中的消息為例。假設(shè)我們生成如下的一個(gè)消息 Test1:

Test1.id = 10; Test1.str = “hello”;

則最終的 Message Buffer 中有兩個(gè) Key-Value 對(duì),一個(gè)對(duì)應(yīng)消息中的 id;另一個(gè)對(duì)應(yīng) str。

Key 用來標(biāo)識(shí)具體的 field,在解包的時(shí)候,Protocol Buffer 根據(jù) Key 就可以知道相應(yīng)的 Value 應(yīng)該對(duì)應(yīng)于消息中的哪一個(gè) field。

Key 的定義如下:

(field_number << 3) | wire_type

可以看到 Key 由兩部分組成。第一部分是 field_number,比如消息 lm.helloworld 中 field id 的 field_number 為 1。第二部分為 wire_type。表示 Value 的傳輸類型。

Wire Type 可能的類型如下表所示:

表 1. Wire Type
TypeMeaningUsed For
0Varintint32, int64, uint32, uint64, sint32, sint64, bool, enum
164-bitfixed64, sfixed64, double
2Length-delimistring, bytes, embedded messages, packed repeated fields
3Start groupGroups (deprecated)
4End groupGroups (deprecated)
532-bitfixed32, sfixed32, float

在我們的例子當(dāng)中,field id 所采用的數(shù)據(jù)類型為 int32,因此對(duì)應(yīng)的 wire type 為 0。細(xì)心的讀者或許會(huì)看到在 Type 0 所能表示的數(shù)據(jù)類型中有 int32 和 sint32 這兩個(gè)非常類似的數(shù)據(jù)類型。Google Protocol Buffer 區(qū)別它們的主要意圖也是為了減少 encoding 后的字節(jié)數(shù)。

在 計(jì)算機(jī)內(nèi),一個(gè)負(fù)數(shù)一般會(huì)被表示為一個(gè)很大的整數(shù),因?yàn)橛?jì)算機(jī)定義負(fù)數(shù)的符號(hào)位為數(shù)字的最高位。如果采用 Varint 表示一個(gè)負(fù)數(shù),那么一定需要 5 個(gè) byte。為此 Google Protocol Buffer 定義了 sint32 這種類型,采用 zigzag 編碼。

Zigzag 編碼用無符號(hào)數(shù)來表示有符號(hào)數(shù)字,正數(shù)和負(fù)數(shù)交錯(cuò),這就是 zigzag 這個(gè)詞的含義了。

如圖所示:

圖 8. ZigZag 編碼

使用 zigzag 編碼,絕對(duì)值小的數(shù)字,無論正負(fù)都可以采用較少的 byte 來表示,充分利用了 Varint 這種技術(shù)。

其他的數(shù)據(jù)類型,比如字符串等則采用類似數(shù)據(jù)庫中的 varchar 的表示方法,即用一個(gè) varint 表示長(zhǎng)度,然后將其余部分緊跟在這個(gè)長(zhǎng)度部分之后即可。

通過以上對(duì) protobuf Encoding 方法的介紹,想必您也已經(jīng)發(fā)現(xiàn) protobuf 消息的內(nèi)容小,適于網(wǎng)絡(luò)傳輸。假如您對(duì)那些有關(guān)技術(shù)細(xì)節(jié)的描述缺乏耐心和興趣,那么下面這個(gè)簡(jiǎn)單而直觀的比較應(yīng)該能給您更加深刻的印象。

對(duì)于代碼清單 1 中的消息,用 Protobuf 序列化后的字節(jié)序列為:

08 65 12 06 48 65 6C 6C 6F 77

而如果用 XML,則類似這樣:

31 30 31 3C 2F 69 64 3E 3C 6E 61 6D 65 3E 68 65 6C 6C 6F 3C 2F 6E 61 6D 65 3E 3C 2F 68 65 6C 6C 6F 77 6F 72 6C 64 3E 一共 55 個(gè)字節(jié),這些奇怪的數(shù)字需要稍微解釋一下,其含義用 ASCII 表示如下:<helloworld> <id>101</id> <name>hello</name> </helloworld>

封解包的速度

首先我們來了解一 下 XML 的封解包過程。XML 需要從文件中讀取出字符串,再轉(zhuǎn)換為 XML 文檔對(duì)象結(jié)構(gòu)模型。之后,再從 XML 文檔對(duì)象結(jié)構(gòu)模型中讀取指定節(jié)點(diǎn)的字符串,最后再將這個(gè)字符串轉(zhuǎn)換成指定類型的變量。這個(gè)過程非常復(fù)雜,其中將 XML 文件轉(zhuǎn)換為文檔對(duì)象結(jié)構(gòu)模型的過程通常需要完成詞法文法分析等大量消耗 CPU 的復(fù)雜計(jì)算。

反觀 Protobuf,它只需要簡(jiǎn)單地將一個(gè)二進(jìn)制序列,按照指定的格式讀取到 C++ 對(duì)應(yīng)的結(jié)構(gòu)類型中就可以了。從上一節(jié)的描述可以看到消息的 decoding 過程也可以通過幾個(gè)位移操作組成的表達(dá)式計(jì)算即可完成。速度非???。

為了說明這并不是我拍腦袋隨意想出來的說法,下面讓我們簡(jiǎn)單分析一下 Protobuf 解包的代碼流程吧。

以代碼清單 3 中的 Reader 為例,該程序首先調(diào)用 msg1 的 ParseFromIstream 方法,這個(gè)方法解析從文件讀入的二進(jìn)制數(shù)據(jù)流,并將解析出來的數(shù)據(jù)賦予 helloworld 類的相應(yīng)數(shù)據(jù)成員。

該過程可以用下圖表示:

圖 9. 解包流程圖

整個(gè)解析過程需要 Protobuf 本身的框架代碼和由 Protobuf 編譯器生成的代碼共同完成。Protobuf 提供了基類 Message 以及 Message_lite 作為通用的 Framework,,CodedInputStream 類,WireFormatLite 類等提供了對(duì)二進(jìn)制數(shù)據(jù)的 decode 功能,從 5.1 節(jié)的分析來看,Protobuf 的解碼可以通過幾個(gè)簡(jiǎn)單的數(shù)學(xué)運(yùn)算完成,無需復(fù)雜的詞法語法分析,因此 ReadTag() 等方法都非常快。 在這個(gè)調(diào)用路徑上的其他類和方法都非常簡(jiǎn)單,感興趣的讀者可以自行閱讀。 相對(duì)于 XML 的解析過程,以上的流程圖實(shí)在是非常簡(jiǎn)單吧?這也就是 Protobuf 效率高的第二個(gè)原因了

?

參考:http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/

轉(zhuǎn)載于:https://www.cnblogs.com/lzzhang/p/4801672.html

總結(jié)

以上是生活随笔為你收集整理的probuf了解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。