### 第一个例子

http://martin.kleppmann.com/2012/12/05/schema-evolution-in-avro-protocol-buffers-thrift.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 = ["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 有比较好的使用。由于历史原因,通信领域几乎没有其他语言可供选择。