Carson帶你學序列化:Protocol Buffer序列化原理大揭秘-為什么性能這么好?


前言

  • 習慣用 Json病涨、XML 數(shù)據(jù)存儲格式的你們熬北,相信大多都沒聽過Protocol Buffer
  • Protocol Buffer 其實 是 Google出品的一種輕量 & 高效的結(jié)構(gòu)化數(shù)據(jù)存儲格式疙描,性能比 Json、XML 真的強讶隐!太起胰!多!

由于 Google出品巫延,我相信Protocol Buffer已經(jīng)具備足夠的吸引力

  • 今天效五,我將講解為什么Protocol Buffer的性能如此的好:
    a. 序列化速度 & 反序列化速度快
    b. 數(shù)據(jù)壓縮效果好,即序列化后的數(shù)據(jù)量體積小

Carson帶你學序列化Protocol Buffer系列文章
快來看看Google出品的Protocol Buffer炉峰,別只會用Json和XML了
Carson帶你學序列化:手把手教你如何安裝Protocol Buffer
Carson帶你學序列化:全面詳解ProtocolBuffer語法
Carson帶你學序列化:Google出品的序列化神器Protocol Buffer使用指南
Carson帶你學序列化:Protocol Buffer序列化原理大揭秘-為什么性能這么好畏妖?
Carson帶你學序列化:深入源碼分析Protocol Buffer
Carson帶你學序列化:深入分析JSON多種解析方式(Gson、AS自帶org.json疼阔、Jackson)
Carson帶你學序列化:深入分析XML多種解析方式(DOM戒劫、SAX、PULL)


目錄

目錄

1. 定義

一種 結(jié)構(gòu)化數(shù)據(jù) 的數(shù)據(jù)存儲格式(類似于 XML婆廊、Json

  1. Google 出品 (開源)
  2. Protocol Buffer 目前有兩個版本:proto2proto3
  3. 因為proto3 還是beta 版迅细,所以本次講解是 proto2

2. 作用

通過將 結(jié)構(gòu)化的數(shù)據(jù) 進行 串行化(序列化),從而實現(xiàn) 數(shù)據(jù)存儲 / RPC 數(shù)據(jù)交換的功能

  1. 序列化: 將 數(shù)據(jù)結(jié)構(gòu)或?qū)ο?轉(zhuǎn)換成 二進制串 的過程
  2. 反序列化:將在序列化過程中所生成的二進制串 轉(zhuǎn)換成 數(shù)據(jù)結(jié)構(gòu)或者對象 的過程

3. 特點

  • 對比于 常見的 XML淘邻、Json 數(shù)據(jù)存儲格式茵典,Protocol Buffer有如下特點:
Protocol Buffer 特點

4. 應(yīng)用場景

傳輸數(shù)據(jù)量大 & 網(wǎng)絡(luò)環(huán)境不穩(wěn)定 的數(shù)據(jù)存儲、RPC 數(shù)據(jù)交換 的需求場景

如 即時IM (QQ宾舅、微信)的需求場景


總結(jié)

傳輸數(shù)據(jù)量較大的需求場景下统阿,Protocol BufferXML、Json 更小筹我、更快扶平、使用 & 維護更簡單!


5. 使用流程

關(guān)于 Protocol Buffer 的使用流程崎溃,具體請看我寫的文章:快來看看Google出品的Protocol Buffer蜻直,別只會用Json和XML了


6. 知識基礎(chǔ)

6.1 網(wǎng)絡(luò)通信協(xié)議

  • 序列化 & 反序列化 屬于通訊協(xié)議的一部分
  • 通訊協(xié)議采用分層模型:TCP/IP模型(四層) & OSI 模型 (七層)
通信協(xié)議結(jié)構(gòu)
  • 序列化 / 反序列化 屬于 TCP/IP模型 應(yīng)用層 和 OSI`模型 展示層的主要功能:

    1. (序列化)把 應(yīng)用層的對象 轉(zhuǎn)換成 二進制串
    2. (反序列化)把 二進制串 轉(zhuǎn)換成 應(yīng)用層的對象
  • 所以, Protocol Buffer屬于 TCP/IP模型的應(yīng)用層 & OSI模型的展示層

6.2 數(shù)據(jù)結(jié)構(gòu)、對象與二進制串

不同的計算機語言中概而,數(shù)據(jù)結(jié)構(gòu)呼巷,對象以及二進制串的表示方式并不相同。

a. 對于數(shù)據(jù)結(jié)構(gòu)和對象

  • 對于面向?qū)ο蟮恼Z言(如Java):對象 = Object = 類的實例化赎瑰;在Java中最接近數(shù)據(jù)結(jié)構(gòu) 即 POJOPlain Old Java Object)王悍,或Javabean(只有 setter/getter 方法的類)

  • 對于半面向?qū)ο蟮恼Z言(如C++),對象 = class餐曼,數(shù)據(jù)結(jié)構(gòu) = struct

b. 二進制串

  • 對于C++压储,因為具有內(nèi)存操作符,所以 二進制串 容易理解:C++的字符串可以直接被傳輸層使用源譬,因為其本質(zhì)上就是以 '\0' 結(jié)尾的存儲在內(nèi)存中的二進制串
  • 對于 Java集惋,二進制串 = 字節(jié)數(shù)組 =byte[]
  1. byte 屬于 Java 的八種基本數(shù)據(jù)類型
  2. 二進制串 容易和 String混淆:String 一種特殊對象(Object)。對于跨語言間的通訊踩娘,序列化后的數(shù)據(jù)當然不能是某種語言的特殊數(shù)據(jù)類型刮刑。

6.3 T - L - V 的數(shù)據(jù)存儲方式

  • 定義
    Tag - Length - Value標識 - 長度 - 字段值 存儲方式
  • 作用
    標識 - 長度 - 字段值 表示單個數(shù)據(jù)养渴,最終將所有數(shù)據(jù)拼接成一個 字節(jié)流雷绢,從而 實現(xiàn) 數(shù)據(jù)存儲 的功能

其中 Length可選存儲,如 儲存Varint編碼數(shù)據(jù)就不需要存儲Length

  • 示意圖
最終存儲的字節(jié)流
  • 優(yōu)點
    從上圖可知理卑,T - L - V 存儲方式的優(yōu)點是
    1. 不需要分隔符 就能 分隔開字段翘紊,減少了 分隔符 的使用
    2. 各字段 存儲得非常緊湊,存儲空間利用率非常高
    3. 若 字段沒有被設(shè)置字段值藐唠,那么該字段在序列化時的數(shù)據(jù)中是完全不存在的帆疟,即不需要編碼

相應(yīng)字段在解碼時才會被設(shè)置為默認值


7. 序列化原理解析

請記住Protocol Buffer三個關(guān)于數(shù)據(jù)存儲 的重要結(jié)論:

  • 結(jié)論1
    Protocol Buffer將 消息里的每個字段 進行編碼后,再利用T - L - V 存儲方式 進行數(shù)據(jù)的存儲宇立,最終得到的是一個 二進制字節(jié)流

序列化 = 對數(shù)據(jù)進行編碼 + 存儲

  • 結(jié)論2
    Protocol Buffer對于不同數(shù)據(jù)類型 采用不同的 序列化方式(編碼方式 & 數(shù)據(jù)存儲方式)鸯匹,如下圖:
    數(shù)據(jù)類型 對應(yīng)的編碼方式

從上表可以看出:

  1. 對于存儲Varint編碼數(shù)據(jù),就不需要存儲字節(jié)長度 Length泄伪,所以實際上Protocol Buffer的存儲方式是 T - V
  2. Protocol Buffer采用其他編碼方式(如LENGTH_DELIMITED)則采用T - L - V
  • 結(jié)論3:因為 Protocol Buffer對于數(shù)據(jù)字段值的 獨特編碼方式 & T - L - V數(shù)據(jù)存儲方式匿级,使得 Protocol Buffer序列化后數(shù)據(jù)量體積如此小

下面蟋滴,我將對不同的編碼方式 & 數(shù)據(jù)存儲方式進行逐一講解。


7.1 Wire Type = 0時的編碼 & 數(shù)據(jù)存儲方式

Wire Type = 0時的編碼 & 數(shù)據(jù)存儲方式

7.1.1 編碼方式: Varint & Zigzag

A. Varint編碼方式介紹

i. 簡介

  • 定義:一種變長的編碼方式
  • 原理:用字節(jié) 表示 數(shù)字:值越小的數(shù)字痘绎,使用越少的字節(jié)數(shù)表示
  • 作用:通過減少 表示數(shù)字 的字節(jié)數(shù) 從而進行數(shù)據(jù)壓縮

如:

  • 對于 int32 類型的數(shù)字津函,一般需要 4個字節(jié) 表示;
  1. 若采用 Varint編碼孤页,對于很小的 int32 類型 數(shù)字尔苦,則可以用 1個字節(jié) 來表示
  2. 雖然大的數(shù)字會需要 5 個 字節(jié) 來表示,但大多數(shù)情況下,消息都不會有很大的數(shù)字允坚,所以采用 Varint方法總是可以用更少的字節(jié)數(shù)來表示數(shù)字

ii. 原理介紹

  • 源碼分析
private void writeVarint32(int n) {                                                                                    
  int idx = 0;  
  while (true) {  
    if ((n & ~0x7F) == 0) {  
      i32buf[idx++] = (byte)n;  
      break;  
    } else {  
      i32buf[idx++] = (byte)((n & 0x7F) | 0x80);  
      // 步驟1:取出字節(jié)串末7位
      // 對于上述取出的7位:在最高位添加1構(gòu)成一個字節(jié)
      // 如果是最后一次取出魂那,則在最高位添加0構(gòu)成1個字節(jié)

      n >>>= 7;  
      // 步驟2:通過將字節(jié)串整體往右移7位,繼續(xù)從字節(jié)串的末尾選取7位稠项,直到取完為止涯雅。
    }  
  }  
  trans_.write(i32buf, 0, idx); 
      // 步驟3: 將上述形成的每個字節(jié) 按序拼接 成一個字節(jié)串
      // 即該字節(jié)串就是經(jīng)過Varint編碼后的字節(jié)
}   

從上面可看出:Varint 中每個 字節(jié) 的最高位 都有特殊含義:

  • 如果是 1,表示后續(xù)的 字節(jié) 也是該數(shù)字的一部分
  • 如果是 0展运,表示這是最后一個字節(jié)活逆,且剩余 7位 都用來表示數(shù)字

所以,當使用Varint解碼時時拗胜,只要讀取到最高位為0的字節(jié)時蔗候,就表示已經(jīng)是Varint的最后一個字節(jié)

因此:

  • 小于 128 的數(shù)字 都可以用 1個字節(jié) 表示;
  • 大于 128 的數(shù)字埂软,比如 300锈遥,會用兩個字節(jié)來表示:10101100 00000010

下面,我將用兩個個例子來說明Varint編碼方式的使用

  • 目的:對 數(shù)據(jù)類型為Int32 的 字段值為296 和字段值為104 進行Varint編碼
  • 以下是編碼過程
Varint編碼過程

從上面可以看出:

  • 對于 int32 類型的數(shù)字仰美,一般需要 4 個字節(jié) 來表示迷殿;
  • 但采用 Varint 方法,對于很小的 Int32 類型 數(shù)字(小于256)咖杂,則可以用 1個字節(jié) 來表示庆寺;

以此類推,比如300也只需要2個字節(jié)

  • 雖然大的數(shù)字會需要 5 個字節(jié) 來表示诉字,但大多數(shù)情況下懦尝,消息都不會有很大的數(shù)字
  • 所以采用 Varint 方法總是可以用更少的字節(jié)數(shù)來表示數(shù)字,從而更好地實現(xiàn)數(shù)據(jù)壓縮

下面繼續(xù)看如何解析經(jīng)過Varint 編碼的字節(jié)

Varint 編碼方式的不足

  • 背景:在計算機內(nèi)壤圃,負數(shù)一般會被表示為很大的整數(shù)

因為計算機定義負數(shù)的符號位為數(shù)字的最高位

  • 問題:如果采用 Varint編碼方式 表示一個負數(shù)陵霉,那么一定需要 5 個 byte(因為負數(shù)的最高位是1,會被當做很大的整數(shù)去處理)
  • 解決方案: Protocol Buffer 定義了 sint32 / sint64 類型表示負數(shù)伍绳,通過先采用 Zigzag 編碼(將 有符號數(shù) 轉(zhuǎn)換成 無符號數(shù))踊挠,再采用 Varint編碼,從而用于減少編碼后的字節(jié)數(shù)
    表示負數(shù)時采用Zigzag編碼
  1. 對于int32 / int64 類型的字段值(正數(shù))冲杀,Protocol Buffer直接采用 Varint編碼
  2. 對于sint32 / sint64 類型的字段值(負數(shù))效床,Protocol Buffer會先采用 Zigzag 編碼,再采用 Varint編碼
  • 總結(jié):為了更好地減少 表示負數(shù)時 的字節(jié)數(shù)权谁,Protocol BufferVarint編碼上又增加了Zigzag 編碼方式進行編碼
  • 下面將詳細介紹 Zigzag編碼方式

B. Zigzag編碼方式詳解

i. 簡介

  • 定義:一種變長的編碼方式
  • 原理:使用 無符號數(shù) 來表示 有符號數(shù)字剩檀;
  • 作用:使得絕對值小的數(shù)字都可以采用較少 字節(jié) 來表示;

特別是對 表示負數(shù)的數(shù)據(jù) 能更好地進行數(shù)據(jù)壓縮

b. 原理

  • 源碼分析

public int int_to_zigzag(int n)
// 傳入的參數(shù)n = 傳入字段值的二進制表示(此處以負數(shù)為例)
// 負數(shù)的二進制 = 符號位為1旺芽,剩余的位數(shù)為 該數(shù)絕對值的原碼按位取反沪猴;然后整個二進制數(shù)+1
{
        return (n <<1) ^ (n >>31);   
        // 對于sint 32 數(shù)據(jù)類型辐啄,使用Zigzag編碼過程如下:
        // 1. 將二進制表示數(shù) 左移1位(左移 = 整個二進制左移,低位補0)
        // 2. 將二進制表示數(shù) 右移31位 
              // 對于右移:
              // 首位是1的二進制(有符號數(shù))运嗜,是算數(shù)右移壶辜,即右移后左邊補1
              // 首位是0的二進制(無符號數(shù)),是邏輯左移洗出,即右移后左邊補0
        // 3. 將上述二者進行異或

        // 對于sint 64 數(shù)據(jù)類型 則為: return  (n << 1> ^ (n >> 63) 士复;
}


// 附:將Zigzag值解碼為整型值
public int zigzag_to_int(int n) 
{
        return(n >>> 1) ^ -(n & 1);
// 右移時,需要用不帶符號的移動翩活,否則如果第一位數(shù)據(jù)位是1的話阱洪,就會補1
}
 

  • 實例說明:將 -2進行 Zigzag編碼:
Zigzag編碼
  • Zigzag 編碼 是補充 Varint編碼在 表示負數(shù) 的不足,從而更好的幫助 Protocol Buffer進行數(shù)據(jù)的壓縮
  • 所以菠镇,如果提前預(yù)知字段值是可能取負數(shù)的時候冗荸,記得采用sint32 / sint64 數(shù)據(jù)類型

總結(jié)

Protocol Buffer 通過VarintZigzag編碼后大大減少了字段值占用字節(jié)數(shù)。

7.1.2 存儲方式:T - V

  • 消息字段的標識號利耍、數(shù)據(jù)類型 & 字段值經(jīng)過 Protocol Buffer采用 Varint & Zigzag 編碼后蚌本,以 T - V 方式進行數(shù)據(jù)存儲

對于 Varint & Zigzag 編碼,省略了T - L - V中的字節(jié)長度Length

Varint & Zigzag數(shù)據(jù)存儲方式

下面將詳細介紹T - V 存儲方式中的存儲細節(jié):Tag & Value

1. Tag

  • 定義:經(jīng)過 Protocol Buffer采用Varint & Zigzag編碼后 的消息字段 標識號 & 數(shù)據(jù)類型 的值
  • 作用:標識 字段
  1. 存儲了字段的標識號(field_number)和 數(shù)據(jù)類型(wire_type)隘梨,即Tag = 字段數(shù)據(jù)類型(wire_type) + 標識號(field_number
  2. 占用 一個字節(jié) 的長度(如果標識號超過了16程癌,則占用多一個字節(jié)的位置)
  3. 解包時,Protocol Buffer根據(jù) TagValue 對應(yīng)于消息中的 字段
  • 具體使用
// Tag 的具體表達式如下
 Tag  = (field_number << 3) | wire_type
// 參數(shù)說明:
// field_number:對應(yīng)于 .proto文件中消息字段的標識號轴猎,表示這是消息里的第幾個字段
// field_number << 3:表示 field_number = 將 Tag的二進制表示 右移三位 后的值 
// field_num左移3位不會導(dǎo)致數(shù)據(jù)丟失嵌莉,因為表示范圍還是足夠大地去表示消息里的字段數(shù)目

//  wire_type:表示 字段 的數(shù)據(jù)類型
//  wire_type = Tag的二進制表示 的最低三位值
//   wire_type的取值
 enum WireType { 
      WIRETYPE_VARINT = 0, 
      WIRETYPE_FIXED64 = 1, 
      WIRETYPE_LENGTH_DELIMITED = 2, 
      WIRETYPE_START_GROUP = 3, 
      WIRETYPE_END_GROUP = 4, 
      WIRETYPE_FIXED32 = 5
   };

// 從上面可以看出,`wire_type`最多占用 3位 的內(nèi)存空間(因為 3位 足以表示 0-5 的二進制)

//  以下是 wire_type 對應(yīng)的 數(shù)據(jù)類型 表
wire_type對應(yīng)數(shù)據(jù)類型
  • 實例說明
// 消息對象
 message person
 { 
    required int32     id = 1;  
    // wire type = 0捻脖,field_number =1 
    required string    name = 2;  
    // wire type = 2锐峭,field_number =2 
  }

//  如果一個Tag的二進制 = 0001 0010
標識號 = field_number = field_number  << 3 =右移3位 =  0000 0010 = 2
數(shù)據(jù)類型 = wire_type = 最低三位表示 = 010 = 2

2. Value

經(jīng)過 Protocol Buffer采用Varint & Zigzag編碼后 的消息字段的值

7.1.3 實例說明

下面通過一個實例進行整個編碼過程的說明:

  • 消息說明
message Test
{

required int32 id1 = 1;

required int32 id2 = 2可婶;
}

// 在代碼中給id1 附上1個字段值:296
// 在代碼中給id2 附上1個字段值:296
Test.setId1(300)沿癞;
Test.setId2(296);

// 編碼結(jié)果為:二進制字節(jié)流 = [8矛渴,-84椎扬,2,16, -88, 2]
  • 整個編碼過程如下
編碼過程

7.2 Wire Type = 1& 5時的編碼&數(shù)據(jù)存儲方式

Wire Type = 1& 5時的編碼&數(shù)據(jù)存儲方式
  • 64(32)-bit編碼方式較簡單:編碼后的數(shù)據(jù)具備固定大小 = 64位(8字節(jié)) / 32位(4字節(jié))

兩種情況下具温,都是高位在后盗舰,低位在前

  • 采用T - V方式進行數(shù)據(jù)存儲,同上桂躏。

7.3 Wire Type = 2時的 編碼 & 數(shù)據(jù)存儲方式

Wire Type = 2時的編碼&數(shù)據(jù)存儲方式
  • 對于編碼方式:
編碼方式
  • 數(shù)據(jù)存儲方式: T - L - V
數(shù)據(jù)存儲示意圖

此處主要講解三種數(shù)據(jù)類型:

  • String類型
  • 嵌套消息類型(Message
  • 通過packed修飾的 repeat 字段(即packed repeated fields

1. String類型

字段值(即V) 采用UTF-8編碼

編碼 & 存儲方式
  • 例子:
message Test2
{
    required string str = 2;
}

// 將str設(shè)置為:testing
Test2.setStr(“testing”)

// 經(jīng)過protobuf編碼序列化后的數(shù)據(jù)以二進制的方式輸出
// 輸出為:18, 7, 116, 101, 115, 116, 105, 110, 103

實例

2. 嵌套消息類型(Message)

  • 存儲方式:T - L - V
  1. 外部消息的 V即為 嵌套消息的字段
  2. T - L -V 里嵌套了一系列的 T - L -V
  • 編碼方式:字段值(即V) 根據(jù)字段的數(shù)據(jù)類型采用不同編碼方式
編碼 & 存儲方式
  • 實例
    定義如下嵌套消息:
message Test2
{
    required string str = 1;
    required int32 id1 = 2;

    
}

message Test3 {
  required Test2 c = 1;
}

// 將Test2中的字段str設(shè)置為:testing
// 將Test2中的字段id1設(shè)置為:296
// 編碼后的字節(jié)為:10 川陆,12 剂习,18,7,116, 101, 115, 116, 105, 110, 103鳞绕,16失仁,-88,2

編碼 & 存儲方式如下

編碼 & 存儲方式

3. 通過packed修飾的 repeat 字段

repeated 修飾的字段有兩種表達方式:

message Test
{
    repeated int32 Car = 4 ;
    // 表達方式1:不帶packed=true

    repeated int32 Car = 4 [packed=true];
    // 表達方式2:帶packed=true
    // proto 2.1 開始可使用

// 區(qū)別在于:是否連續(xù)存儲repeated類型數(shù)據(jù)
}


// 在代碼中給`repeated int32 Car`附上3個字段值:3们何、270萄焦、86942

Test.setCar(3);
Test.setCar(270)冤竹;
Test.setCar(86942)拂封;
  • 背景:對于同一個 repeated字段、多個字段值來說鹦蠕,他們的Tag都是相同的冒签,即數(shù)據(jù)類型 & 標識號都相同

repeated類型可以看成是數(shù)組

  • 問題:若以傳統(tǒng)的多個 T - V對存儲(不帶packed=true),則會導(dǎo)致Tag的冗余钟病,即相同的Tag存儲多次萧恕;
不帶pack的存儲方式
  • 解決方案:采用帶packed=truerepeated 字段存儲方式,即將相同的 Tag 只存儲一次肠阱、添加 repeated 字段下所有字段值的長度Length票唆、連續(xù)存儲 repeated 字段值,組成一個大的Tag - Length - Value -Value -Value對屹徘,即T - L - V - V - V對走趋。
帶pack的存儲方式

通過采用帶packed=truerepeated 字段存儲方式,從而更好地壓縮序列化后的數(shù)據(jù)長度缘回。

特別注意

  • Protocol Bufferpacked修飾只用于repeated字段 或 基本類型的repeated字段
  • 用在其他字段吆视,編譯 .proto 文件時會報錯

8. 特別注意

  • 注意1:若 required字段沒有被設(shè)置字段值,那么在IsInitialized()進行初始化檢查會報錯并提示失敗

所以 required字段必須要被設(shè)置字段值

  • 注意2:序列化順序 是根據(jù) Tag標識號 從小到大 進行編碼

.proto文件內(nèi) 字段定義的數(shù)據(jù)無關(guān)

  • 注意3:T - V的數(shù)據(jù)存儲方式保證了Protobuf的版本兼容:高<->低 或 低<->高都可以適配

若新版本 增加了 required 字段酥宴, 舊版本 在數(shù)據(jù)解碼時會認為IsInitialized() 失敗啦吧,所以慎用 required字段


9. 使用建議

根據(jù)上面的序列化原理分析,我總結(jié)出以下Protocol Buffer的使用建議

通過下面建議能有效降低序列化后數(shù)據(jù)量的大小

  • 建議1:多用 optionalrepeated修飾符
    因為若optionalrepeated 字段沒有被設(shè)置字段值拙寡,那么該字段在序列化時的數(shù)據(jù)中是完全不存在的授滓,即不需要進行編碼

相應(yīng)的字段在解碼時才會被設(shè)置為默認值

  • 建議2:字段標識號(Field_Number)盡量只使用 1-15,且不要跳動使用
    因為Tag里的Field_Number是需要占字節(jié)空間的肆糕。如果Field_Number>16時般堆,Field_Number的編碼就會占用2個字節(jié),那么Tag在編碼時也就會占用更多的字節(jié)诚啃;如果將字段標識號定義為連續(xù)遞增的數(shù)值淮摔,將獲得更好的編碼和解碼性能

  • 建議3:若需要使用的字段值出現(xiàn)負數(shù),請使用 sint32 / sint64始赎,不要使用int32 / int64
    因為采用sint32 / sint64數(shù)據(jù)類型表示負數(shù)時和橙,會先采用Zigzag編碼再采用Varint編碼仔燕,從而更加有效壓縮數(shù)據(jù)

  • 建議4:對于repeated字段,盡量增加packed=true修飾
    因為加了packed=true修飾repeated字段采用連續(xù)數(shù)據(jù)存儲方式魔招,即T - L - V - V -V方式


10. 序列化 & 反序列化過程

  • Protocol Buffer除了序列化 & 反序列化后的數(shù)據(jù)體積小晰搀,序列化 & 反序列化的速度也非常快
  • 下面我將講解序列化 & 反序列化的序列化過程

10.1 Protocol Buffer的序列化 & 反序列化過程

序列化過程如下:

  1. 判斷每個字段是否有設(shè)置值办斑,有值才進行編碼
  2. 根據(jù) 字段標識號&數(shù)據(jù)類型 將 字段值 通過不同的編碼方式進行編碼

由于:
a. 編碼方式簡單(只需要簡單的數(shù)學運算 = 位移等等)
b. 采用 Protocol Buffer 自身的框架代碼 和 編譯器 共同完成

所以Protocol Buffer的序列化速度非惩馑。快。

反序列化過程如下:

  1. 調(diào)用 消息類的 parseFrom(input) 解析從輸入流讀入的二進制字節(jié)數(shù)據(jù)流

從上面可知乡翅,Protocol Buffer解析過程只需要通過簡單的解碼方式即可完成鳞疲,無需復(fù)雜的詞法語法分析,因此 解析速度非陈屠剩快建丧。

  1. 將解析出來的數(shù)據(jù) 按照指定的格式讀取到 JavaC++波势、Phyton 對應(yīng)的結(jié)構(gòu)類型中

由于:
a. 解碼方式簡單(只需要簡單的數(shù)學運算 = 位移等等)
b. 采用 Protocol Buffer 自身的框架代碼 和 編譯器 共同完成

所以Protocol Buffer的反序列化速度非臭嶂欤快。

10.2 對比于XML 的序列化 & 反序列化過程

XML的反序列化過程如下:

  1. 從文件中讀取出字符串
  2. 將字符串轉(zhuǎn)換為 XML 文檔對象結(jié)構(gòu)模型
  3. XML 文檔對象結(jié)構(gòu)模型中讀取指定節(jié)點的字符串
  4. 將該字符串轉(zhuǎn)換成指定類型的變量

上述過程非常復(fù)雜尺铣,其中拴曲,將 XML 文件轉(zhuǎn)換為文檔對象結(jié)構(gòu)模型的過程通常需要完成詞法文法分析等大量消耗 CPU 的復(fù)雜計算。

因為序列化 & 反序列化過程簡單凛忿,所以序列化 & 反序列化過程速度非吵鹤疲快,這也是 Protocol Buffer效率高的原因


11.總結(jié)

Protocol Buffer的性能好店溢,主要體現(xiàn)在 序列化后的數(shù)據(jù)體積小 & 序列化速度快叁熔,最終使得傳輸效率高,其原因如下:


歡迎關(guān)注Carson_Ho的簡書

不定期分享關(guān)于安卓開發(fā)的干貨踏堡,追求短猎唁、平、快顷蟆,但卻不缺深度胖秒。


請點贊缎患!因為你的鼓勵是我寫作的最大動力!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阎肝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子肮街,更是在濱河造成了極大的恐慌风题,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嫉父,死亡現(xiàn)場離奇詭異沛硅,居然都是意外死亡,警方通過查閱死者的電腦和手機绕辖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門摇肌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人仪际,你說我怎么就攤上這事围小。” “怎么了树碱?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵肯适,是天一觀的道長。 經(jīng)常有香客問我成榜,道長框舔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任赎婚,我火速辦了婚禮刘绣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘挣输。我一直安慰自己纬凤,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布歧焦。 她就那樣靜靜地躺著移斩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绢馍。 梳的紋絲不亂的頭發(fā)上向瓷,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音舰涌,去河邊找鬼猖任。 笑死,一個胖子當著我的面吹牛瓷耙,可吹牛的內(nèi)容都是我干的朱躺。 我是一名探鬼主播刁赖,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼长搀!你這毒婦竟也來了宇弛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤源请,失蹤者是張志新(化名)和其女友劉穎枪芒,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谁尸,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡舅踪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了良蛮。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抽碌。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖决瞳,靈堂內(nèi)的尸體忽然破棺而出货徙,到底是詐尸還是另有隱情,我是刑警寧澤瞒斩,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布破婆,位于F島的核電站,受9級特大地震影響胸囱,放射性物質(zhì)發(fā)生泄漏祷舀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一烹笔、第九天 我趴在偏房一處隱蔽的房頂上張望裳扯。 院中可真熱鬧,春花似錦谤职、人聲如沸饰豺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冤吨。三九已至,卻和暖如春饶套,著一層夾襖步出監(jiān)牢的瞬間漩蟆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工妓蛮, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留怠李,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像捺癞,于是被迫代替她去往敵國和親夷蚊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容