互联网 频道

环信王纯业:ASN 和 PB 的编码效率比较

  本文通过实际例证概括的说明了为什么ASN1 PER的编码效率比PB的高,以及使用ASN1的四大优点和使用ASN1 PER 的三大风险。

  ### 第一个例子

  http://martin.kleppmann.com/20 ... .html 是一个很好的比较例子。

  我类似的做了一个 ASN1 的结构

  ```

  Person DEFINITIONS AUTOMATIC TAGS ::=

  BEGIN

  Person ::= SEQUENCE {

  username PrintableString,

  favouritenumber INTEGER,

  interests SEQUENCE OF PrintableString

  }

  END

  ```

  用下面的方法编译

  ```

  erlc -I. -bper Person.asn

  erl

  > c("Person").

  {ok,'Person'}

  > {ok, B} = 'Person':encode('Person', #'Person'{username = "Martin", favouritenumber = 1337, interests = ["quot;daydreaming", "hacking"]}).

  {ok,<<6,77,97,114,116,105,110,2,5,57,2,11,100,97,121,100,

  114,101,97,109,105,110,103,7,104,97,99,...>>}

  > byte_size(B).

  31

  ```

  这个例子里面,ASN1 用了 31 bytes ,protobuf 用了 33 bytes, Avro 用了

  32 bytes。 这不是一个公平的比较,对于大量使用小数据结构的时候,例如,

  enum command type 之类的,ASN1 可以节省更多的 bytes 。

  ### 第二个例子

  这是 protobuf 的定义。

  ```

  package dummy;

  message S {

  optional int32 a =1;

  optional bool b =2;

  optional int32 c =3;

  optional D d =4;

  }

  message D {

  optional bool d1 = 1;

  optional bool d2 = 2;

  }

  ```

  用 erlang 编译 参考 [https://github.com/tomas-abrahamsson/gpb]()

  ```

  > deps/gpb/bin/protoc-erl -I. -o-erl src -o-hrl include s1.proto

  > erl -sname a@localhost

  (a@localhost)1> R = #'S'{a=1,b=true,c=2, d=#'D'{d1 = true, d2 = true} }.

  #'S'{a =1,b = true,c = 2,d = #'D'{d1 = true,d2 = true}}

  (sync@localhost)2> s1:encode_msg(R).

  <<8,1,16,1,24,2,34,4,8,1,16,1>>

  (a@localhost)14> byte_size(s1:encode_msg(R)).

  12

  ```

  可以看到 protobuf 用了 12 个字节。

  ASN1 的例子,使用 PER 编码方式。

  ```

  Dummy DEFINITIONS AUTOMATIC TAGS ::=

  BEGIN

  Dummy ::= SEQUENCE {

  a INTEGER (0..7),

  b BOOLEAN,

  c INTEGER (0..3),

  d SEQUENCE {

  d1 BOOLEAN,

  d2 BOOLEAN }}

  END

  ```

  ```

  > erlc -I. -bper Dummy.asn

  > erl

  (a@localhost)1> 'Dummy':encode('Dummy', #'Dummy'{ a = 1, b = true, c = 2, d = #'Dummy_d'{d1= true, d2 = true }}).

  {ok,<<";">>}

  ```

  protobuf 用了 12 个字节, ASN1 用了 1 个字节。同样,这也不是一个公平的比较。

  很难做出公平的比较。但是可以说在大多数情况下 ASNPER 的编码是更加节省带宽的。

  ### 为什么 ASN1 PER 的编码效率比 PB 的高?

  1. ASN1 PER 是面向 bit 的编码方式,PB 是面向字节的编码方式。

  2. PB 中 message 都是可以扩展的,ASN1 中只有使用 `...` 关键字的类型,才是可以扩展的。

  3. PB 中的整数很简单,都是可以扩展到 64 位,ASN1 中有更加灵活(复杂) 的整数扩展方式。

  2. PB 中的 `required`, `optional`, `oneof`, 和 `extensions` 的特性,对编码没有影响。例如,就算是 `required` 的字段,编码的时候,也是需要 tag 。

  3. ASN1 PER 对很多关键字都是敏感的。例如

  1. `required` 的字段不会添加表明类型的 tag

  2. `required` 的字段按顺序编码。

  4. tag 在 PER 中不做编码。

  5. by default, every message is extensible in PB. Instread, ASN1 extensibility should be explicitly specified.

  4. PB 中支持的整数类型不支持 subtype, 而 ASN1 PER 中的整数支持 subtype , 可以实现高效编码。

  ### 使用 ASN1 的优点

  1. 编码紧凑,节省带宽。这是为什么几乎所有的 2G/3G/4G/5G 的无线通信协议都使用 ASN1 的原因之一。

  2. ASN1 久经考验,asn1c 的项目已经十多年了,依然活跃开发。 Erlang 因为是通信公司创造的,语言内嵌 ASN 的支持。 Erlang 没有默认支持 PB 需要使用第三方开发库。

  3. ASN1 支持 XER (XML) ,可以方便的调试。

  4. wireshark 本身对 ASN1 的支持很好。

  ### 使用 ASN1 PER 的风险

  1. ASN1 本身很复杂。ASN1 的学习成本高

  2. PER 编码很复杂。可以用700行 C 代码实现 PB 的编解码,但实现 PER 编码不行。

  3. ASN1 对语言的支持不多,似乎只有 C/Erlang 有比较好的使用。由于历史原因,通信领域几乎没有其他语言可供选择。

特别提醒:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
0
相关文章