0%

RPC ProtoBuf框架

ProtoBuf 作为一种跨平台、语言无关、可扩展的序列化结构数据的方法,已广泛应用于网络数据交换及存储。随着互联网的发展,系统的异构性会愈发突出,跨语言的需求会愈加明显,同时 gRPC 也大有取代 Restful 之势,而 ProtoBuf 作为 gRPC 跨语言、高性能的法宝,我们技术人有必要深入理解 ProtoBuf 原理,为以后的技术更新和选型打下基础。

序列化框架

如何评价

我们评判一个编解码框架的优劣时,往往会考虑以下几个因素。

  • 是否支持跨语言,支持的语言种类是否丰富
  • 编码后的码流大小编解码的性能
  • 类库是否小巧,API使用是否方便
  • 使用者需要手工开发的工作量和难度

Java序列化的缺点

基于 Java 提供的对象输入输出流 ObjectInputStream 和 ObjectOutputStream,实现了 Java 对象实例的编解码。但在 RPC 场景下,很少直接使用 Java 序列化进行消息的编解码和传输,主要因为以下这两个方面:

  • 无法跨语言
  • 序列化后码流太大

常用的序列化方式

三种常用序列化方式优缺点,如下所示:

首先我们来看下为什么不使用 XML,尽管 XML 的可读性和可扩展性非常好,也非常适合描述数据结构,但是 XML 解析的时间开销和 XML 为了可读性而牺牲的空间开销都非常大,因此不适合做高性能的通信协议。Protobuf 使用二进制编码,在空间和性能上具有更大的优势。

Protobuf 另一个比较吸引人的地方就是它的数据描述文件和代码生成机制,利用数据描述文件对数据结构进行说明的优点如下:

  • 文本化的数据结构描述语言,可以实现语言和平台无关,特别适合异构系统间的集成
  • 通过标识字段的顺序,可以实现协议的前向兼容
  • 自动代码生成,不需要手工编写同样数据结构的不同语言版本
  • 方便后续的管理和维护。相比于代码,结构化的文档更容易管理和维护。

Google Protobuf

Protobuf 全称 Google Protocol Buffers,它将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的 POJO 对象和 Protobuf 相关的方法和属性。

官方文档见 Protocol Buffers Introduction

API 文档见 Protocol Buffers API Documents

数据类型

基本数据类型

//采用proto3的语法
syntax = "proto3";
//结构体
message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

下表为 protobuf 支持的基本数据类型

.proto Type Notes C++ Type Java Type Go Type
double double double float64
float float float float32
int32 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. int32 int int32
int64 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. int64 long int64
uint32 Uses variable-length encoding. uint32 int[1] uint32
uint64 Uses variable-length encoding. uint64 long[1] uint64
sint32 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. int32 int int32
sint64 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. int64 long int64
fixed32 Always four bytes. More efficient than uint32 if values are often greater than 228. uint32 int[1] uint32
fixed64 Always eight bytes. More efficient than uint64 if values are often greater than 256. uint64 long[1] uint64
sfixed32 Always four bytes. int32 int int32
sfixed64 Always eight bytes. int64 long int64
bool bool boolean bool
string A string must always contain UTF-8 encoded or 7-bit ASCII text, and cannot be longer than 232. string String string
bytes May contain any arbitrary sequence of bytes no longer than 232. string ByteString []byte

枚举类型

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  
  enum Corpus {
  	//Enum's first constant must map to zero, 
  	//so that we can use 0 as a numeric default value.
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
  }
  Corpus corpus = 2;
}

注:定义在其他 message 中的 Enumerator 可通过_MessageType_._EnumType_访问。

配置项:allow_alias

通过配置allow_alias = true可以让不同的枚举项映射到同一个值。

message MyMessage1 {
  enum EnumAllowingAlias {
    option allow_alias = true;
    UNKNOWN = 0;
    STARTED = 1;
    RUNNING = 1;
  }
}
message MyMessage2 {
  enum EnumNotAllowingAlias {
    UNKNOWN = 0;
    STARTED = 1;
    // RUNNING = 1;  // Uncommenting this line will cause a compile error inside Google and a warning message outside.
  }
}

字典类型

map<key_type, value_type> map_field = field_numbers;

key_type 支持任何整型和字符串类型(即,排除 floating point types、enum and bytes).value_type 支持除了 map 类型的任何类型。

详细内容见 Maps

向后兼容

message MapFieldEntry {
  key_type key = 1;
  value_type value = 2;
}

repeated MapFieldEntry map_field = N;

导入类型

通过 import 导入 protobuf 自带的包或者自定义的包,并引用其内部定义的类型。

syntax = "proto3";

import "google/protobuf/wrappers.proto";

message Guide {
  google.protobuf.StringValue str = 1;
}

数据编码

Base 128 Varints

Varints 即可变整型,根据提出的 msb 概念进行数据的切分。

Varints are a method of serializing integers using one or more bytes. Smaller numbers take a smaller number of bytes.

Each byte in a varint, except the last byte, has the most significant bit (msb) set – this indicates that there are further bytes to come. The lower 7 bits of each byte are used to store the two’s complement representation of the number in groups of 7 bits, least significant group first.

例一:计算 01 的值

01 -> 0000 0001
   ->  000 0001 (delete msb)
   -> (varint 1)

例二:计算 9E A7 05 的值

9E A7 05 -> 1001 1110 (original 9E)  1010 0111 (original A7)  0000 0101 (original 05)
         ->  001 1110 (original 9E)   010 0111 (original A7)   000 0101 (original 05)
         ->       101 (original 05) + 010 0111 (original A7) + 001 1110 (original 9E)
         -> 101 010 0111 001 1110
         -> (varint 86942)

简单的来说,msb 为 0 是代表整个可变整型组的最后一个数据,而 msb 为 1 是代表整个可变整型组的第一或中间的数据。

注:对于使用 msb 来分离数据的方式,计算过程其实有两种:

一种是对当前数左移与之前结果进行或操作

result |= data[i] << offset;
offset += 7;

另一种是对当前结果左移与当前数进行或操作

result <<= offset;
result |= data[i];

但是测试结果很神奇!居然是第二种方式计算效率高于第一种?难道 Protobuf 一顿编码猛如虎,实际操作 0-5 ?

Message Structure

message 中字段的编码结构为 (field_number << 3) | wire_type varint

message Request {
	//int32 对应 wire_type
	//1 对应 field_number
	//该字段的值对应 varint
	int32 id = 1;	
}

The available wire types are as follows:

Type Meaning Used For
0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
1 64-bit fixed64, sfixed64, double
2 Length-delimited string, bytes, embedded messages, packed repeated fields
3 Start group groups (deprecated)
4 End group groups (deprecated)
5 32-bit fixed32, sfixed32, float

More Value Types

Signed Integers

我们需要了解 the signed int types (sint32 and sint64) and the “standard” int types (int32 and int64) 的区别。将负数编码时,如果使用 the standard int types,作为结果的变长整型将固定为 10 bytes 的长度。而如果使用 the signed int types,作为结果的变长整型将采用更高效的 ZigZag 编码。

下表为 ZigZag 编码的示例及计算方式

Signed Original Encoded As
0 0
-1 1
1 2
-2 3
2147483647 4294967294
-2147483648 4294967295

sint32中,ZigZag 编码计算方式为

(n << 1) ^ (n >> 31)

sint64中,ZigZag 编码计算方式为

(n << 1) ^ (n >> 63)

Strings

message Test2 {
  optional string b = 2;
}

编码分析

12 07 [74 65 73 74 69 6e 67]

0x12
-> 0001 0010  (binary representation)
-> 00010 010  (regroup bits)
-> field_number = 2, wire_type = 2

0x07
-> 0000 0111
-> group_len = 7

其中,组中的数字为对应字符的 UTF-8 编码。

Embedded Messages

message Test1 {
  optional int32 a = 1;
}

message Test3 {
  optional Test1 c = 3;
}

Test3 编码分析

1a 03 08 96 01

0x1a
-> 00011 010 -> field_number = 3, wire_type = 2
0x03
-> group_len = 3
[08 96 01]
-> 08 -> 0000 1000 -> field_number = 1, wire_type = 0
-> 96 01 -> 150

整体过程可以解释为,为 Test3 设置其成员变量 Test1,为 Test1 中 field_number 为 1 的字段设置值 150。

Optional Elements

在 proto3 语法中,Optional Elements 即不带 repeated 对于一个没有用 repeated 关键字标注的字段,其值只会出现 0 或 1 次。

However, parsers are expected to handle the case in which they do. For numeric types and strings, if the same field appears multiple times, the parser accepts the last value it sees. For embedded message fields, the parser merges multiple instances of the same field, as if with the method – that is, all singular scalar fields in the latter instance replace those in the former, singular embedded messages are merged, and repeated fields are concatenated. The effect of these rules is that parsing the concatenation of two encoded messages produces exactly the same result as if you had parsed the two messages separately and merged the resulting objects. That is, this:Message::MergeFrom

Repeated Elements

message Test4 {
  repeated int32 d = 4 [packed=true];
  //与上相同
  //repeated int32 d = 4
}

在 proto3 语法当中,只有使用到 varint, 32-bit, or 64-bit wire type 的类型可以声明 packed 参数,且默认为 true。其作用主要体现在,当 repeated 字段进行编码时,如果 packed 参数为 true 且不包含任何元素时,不对该字段进行编码。

22        // key (field number 4, wire type 2)
06        // payload size (6 bytes)
03        // first element (varint 3)
8E 02     // second element (varint 270)
9E A7 05  // third element (varint 86942)

Importing Definitions

Note that this feature is not available in Java.

In the above example, the Result message type is defined in the same file as SearchResponse – what if the message type you want to use as a field type is already defined in another .proto file?

You can use definitions from other .proto files by importing them. To import another .proto‘s definitions, you add an import statement to the top of your file:

import "myproject/other_protos.proto";

By default you can only use definitions from directly imported .proto files. However, sometimes you may need to move a .proto file to a new location. Instead of moving the .proto file directly and updating all the call sites in a single change, now you can put a dummy .proto file in the old location to forward all the imports to the new location using the import public notion. import public dependencies can be transitively relied upon by anyone importing the proto containing the import public statement. For example:

// new.proto
// All definitions are moved here
// old.proto
// This is the proto that all clients are importing.
import public "new.proto";
import "other.proto";
// client.proto
import "old.proto";
// You use definitions from old.proto and new.proto, but not other.proto

The protocol compiler searches for imported files in a set of directories specified on the protocol compiler command line using the -I/--proto_path flag. If no flag was given, it looks in the directory in which the compiler was invoked. In general you should set the --proto_path flag to the root of your project and use fully qualified names for all imports.

Using proto2 Message Types

It’s possible to import proto2 message types and use them in your proto3 messages, and vice versa. However, proto2 enums cannot be used directly in proto3 syntax (it’s okay if an imported proto2 message uses them).

Nested Types

message SearchResponse {
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}

可以通过 _Parent_._Type_ 的形式来重用 message 结构体

message SomeOtherMessage {
  SearchResponse.Result result = 1;
}

嵌套的规则就是保证不会有同级的重名 message 结构体即可

message Outer {                  // Level 0
  message MiddleAA {  // Level 1
    message Inner {   // Level 2
      int64 ival = 1;
      bool  booly = 2;
    }
  }
  message MiddleBB {  // Level 1
    message Inner {   // Level 2
      int32 ival = 1;
      bool  booly = 2;
    }
  }
}

更新Message须知

  • 别改变已有的 field numbers

  • 新增字段不会对旧版本 message 有影响。

    最初,proto3 消息在解析过程中总是丢弃未知字段,但在版本 3.5 中,我们重新引入了未知字段的保存,以匹配原始 2 行为。在版本 3.5 及以后,‎‎未知字段在分析过程中保留,并包含在序列化输出中。‎

  • 字段可以被删除,但在后续更新的过程中不能重用其 field_numbers。

    可以使用自定义前缀标识其无效性,或者使用 reserved 关键字占位。

    message Foo {
      reserved 2, 15, 9 to 11;
      reserved "foo", "bar";
    }
  • int32, uint32, int64, uint64, and bool 是相互兼容的。sint32 and sint64 相互兼容,但不与其他 int 类型兼容。string and bytes 在字节长度及编码都为 UTF-8 时相互兼容。fixed32 兼容 sfixed32 并且 fixed64 兼容 sfixed64.

  • Embedded messages 与一个包含 message 编码版本号的 bytes兼容。

  • enum is compatible with int32, uint32, int64, and uint64 in terms of wire format. 兼容是在存储格式上兼容,但反序列化时的展现形式是 language-dependent 的。

  • Changing a single value into a member of a new oneof is safe and binary compatible. Moving multiple fields into a new oneof may be safe if you are sure that no code sets more than one at a time. Moving any fields into an existing oneof is not safe.

Any

message 类型 Any 可以在没有.proto定义的情况下使用其内容

import "google/protobuf/any.proto";

message ErrorStatus {
  string message = 1;
  repeated google.protobuf.Any details = 2;
}

Oneof

如果有一条包含许多字段的消息,并且最多同时设置一个字段,您可以使用其中 oneof 来强制执行此行为并节省内存。

Oneof 字段类似于常规字段,除了 Oneof 共享内存的所有字段之外,最多可以同时设置一个字段。设置 Oneof 的任何成员都会自动清除所有其他成员。您可以使用 case() 或 WhichOneof() 方法检查 Oneof 中的哪个值被设置(如果有的话),具体取决于选择的语言。

使用Oneof

message SampleMessage {
  oneof test_oneof {
    string name = 4;
    SubMessage sub_message = 9;
  }
}

向后兼容性问题

添加或删除 oneof 字段时要小心。如果检查 oneof 的值会返回 None / NOT _ SET,这可能意味着其中一个没有被设置,或者它已经被设置为 oneof 的不同版本中的字段。没有办法区分这两者,因为没有办法知道线上的未知区域是否是oneof的成员。

Tag Reuse Issues

  • Move fields into or out of a oneof: You may lose some of your information (some fields will be cleared) after the message is serialized and parsed. However, you can safely move a single field into a new oneof and may be able to move multiple fields if it is known that only one is ever set.
  • Delete a oneof field and add it back: This may clear your currently set oneof field after the message is serialized and parsed.
  • Split or merge oneof: This has similar issues to moving regular fields.

Packages

通过设置 package 选项,可以防止协议消息类型之间的命名冲突。

package foo.bar;
message Open { ... }

在定义时,可以在定义消息类型的字段上使用包名:

message Foo {
  ...
  foo.bar.Open open = 1;
  ...
}

The way a package specifier affects the generated code depends on your chosen language:

  • In C++ the generated classes are wrapped inside a C++ namespace. For example, Open would be in the namespace foo::bar.
  • In Java, the package is used as the Java package, unless you explicitly provide an option java_package in your .proto file.
  • In Go, the package is used as the Go package name, unless you explicitly provide an option go_package in your .proto file.

Defining Services

如果要将消息类型用于 RPC(远程过程调用)系统,可以在.proto文件中定义 RPC 服务接口和协议缓冲编译器将使用您选择的语言生成服务接口代码和存根。因此,如果您想用一种接受您的搜索请求并返回搜索响应的方法来定义 RPC 服务,您可以在 .proto 文件中定义它。 如下:

service SearchService {
  rpc Search (SearchRequest) returns (SearchResponse);
}

与协议缓冲区一起使用的最直接的 RPC 系统是 gRPC : Google 开发的语言和平台无关的开源 RPC 系统。gRPC 与协议缓冲区配合得特别好,并允许您直接从使用特殊协议缓冲编译插件的 .proto 文件中生成相关的 RPC 代码。

如果你不想使用gRPC,也可以在你自己的RPC实现中使用协议缓冲区。你可以在 Proto2 Language Guide 中找到更多关于这一点的信息。

Options

Options 的全部内容被定义在 google/protobuf/descriptor.proto。下表是 Options 级别:

  • file-level meaning they should be written at the top-level scope
  • message-level meaning they should be written inside message definitions
  • field-level meaning they should be written inside field definitions

Options can also be written on enum types, enum values, oneof fields, service types, and service methods; however, no useful options currently exist for any of these.

详细内容见 Options

JSON Mapping

Proto3 支持转化为 JSON 格式映射。

proto3 JSON JSON example Notes
message object {"fooBar": v, "g": null, …} Generates JSON objects. Message field names are mapped to lowerCamelCase and become JSON object keys. If the json_name field option is specified, the specified value will be used as the key instead. Parsers accept both the lowerCamelCase name (or the one specified by the json_name option) and the original proto field name. null is an accepted value for all field types and treated as the default value of the corresponding field type.
enum string "FOO_BAR" The name of the enum value as specified in proto is used. Parsers accept both enum names and integer values.
map object {"k": v, …} All keys are converted to strings.
repeated V array [v, …] null is accepted as the empty list [].
bool true, false true, false
string string "Hello World!"
bytes base64 string "YWJjMTIzIT8kKiYoKSctPUB+" JSON value will be the data encoded as a string using standard base64 encoding with paddings. Either standard or URL-safe base64 encoding with/without paddings are accepted.
int32, fixed32, uint32 number 1, -10, 0 JSON value will be a decimal number. Either numbers or strings are accepted.
int64, fixed64, uint64 string "1", "-10" JSON value will be a decimal string. Either numbers or strings are accepted.
float, double number 1.1, -10.0, 0, "NaN", "Infinity" JSON value will be a number or one of the special string values “NaN”, “Infinity”, and “-Infinity”. Either numbers or strings are accepted. Exponent notation is also accepted. -0 is considered equivalent to 0.
Any object {"@type": "url", "f": v, … } If the Any contains a value that has a special JSON mapping, it will be converted as follows: {"@type": xxx, "value": yyy}. Otherwise, the value will be converted into a JSON object, and the "@type" field will be inserted to indicate the actual data type.
Timestamp string "1972-01-01T10:00:20.021Z" Uses RFC 3339, where generated output will always be Z-normalized and uses 0, 3, 6 or 9 fractional digits. Offsets other than “Z” are also accepted.
Duration string "1.000340012s", "1s" Generated output always contains 0, 3, 6, or 9 fractional digits, depending on required precision, followed by the suffix “s”. Accepted are any fractional digits (also none) as long as they fit into nano-seconds precision and the suffix “s” is required.
Struct object { … } Any JSON object. See struct.proto.
Wrapper types various types 2, "2", "foo", true, "true", null, 0, … Wrappers use the same representation in JSON as the wrapped primitive type, except that null is allowed and preserved during data conversion and transfer.
FieldMask string "f.fooBar,h" See field_mask.proto.
ListValue array [foo, bar, …]
Value value Any JSON value. Check google.protobuf.Value for details.
NullValue null JSON null
Empty object {} An empty JSON object

JSON options

A proto3 JSON implementation may provide the following options:

  • Emit fields with default values: Fields with default values are omitted by default in proto3 JSON output. An implementation may provide an option to override this behavior and output fields with their default values.
  • Ignore unknown fields: Proto3 JSON parser should reject unknown fields by default but may provide an option to ignore unknown fields in parsing.
  • Use proto field name instead of lowerCamelCase name: By default proto3 JSON printer should convert the field name to lowerCamelCase and use that as the JSON name. An implementation may provide an option to use proto field name as the JSON name instead. Proto3 JSON parsers are required to accept both the converted lowerCamelCase name and the proto field name.
  • Emit enum values as integers instead of strings: The name of an enum value is used by default in JSON output. An option may be provided to use the numeric value of the enum value instead.

生成Skeleton

详细内容见 Generating Your Classes

Java语言Maven方式构建

引入 protobuf 和 grpc 的包

<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>4.0.0-rc-2</version>
</dependency>
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-all</artifactId>
    <version>1.37.0</version>
</dependency>

引入 protobuf-maven-plugin 插件

<build>
    <extensions>
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>1.6.2</version>
        </extension>
    </extensions>
    <plugins>
        <plugin>
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
            <version>0.6.1</version>
            <configuration>
                <protocArtifact>com.google.protobuf:protoc:3.11.0:exe:${os.detected.classifier}</protocArtifact>
                <pluginId>grpc-java</pluginId>
                <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.29.0:exe:${os.detected.classifier}</pluginArtifact>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

执行以下两个命令,默认读取src/main/proto路径下的 proto 文件:

  • protobuf:compile 生成序列化类
  • protobuf:compile-custom 生成 Grpc传输类

注意:IDEA在执行这两个命令之后可能不能正确显示生成的文件,记得Reload from Disk。如果 target 中已有生成的文件但不能正常检索,在 Project Structure 中为 target\generated-sources\protobuf 目录下的 grpc-javajava 文件夹 mark 为 Source 即可。

各序列化框架性能测试

SIMPLE/GENERIC:
Serializes any POJO tree without class specific optimization. Serialized classes are known in advance. No cycle detection/shared object detection is done.

                                   create     ser   deser   total   size  +dfl
java-built-in                          63    5838   30208   36046    889   514
hessian                                63    3881    6176   10057    501   313
kryo                                   63     655     838    1493    212   132
fast-serialization                     63     704     864    1568    252   166
jboss-serialization                    63    6466    6643   13110    932   582
jboss-marshalling-river                63    4656   23892   28548    694   400
protostuff                             82     495     732    1227    239   150
msgpack-databind                       62     830    1370    2200    233   146
json/jackson/databind                  62    1895    2600    4496    485   261
json/jackson/db-afterburner            63    1513    1988    3501    485   261
json/protostuff-runtime                63    1532    2138    3670    469   243
json/google-gson/databind              63    5633    4844   10477    486   259
json/svenson-databind                  63    5270   10358   15628    495   272
json/flexjson/databind                 63   19445   25394   44838    503   273
json/fastjson/databind                 63    1316    1149    2465    486   262
smile/jackson/databind                 63    1768    1891    3659    338   241
smile/jackson/db-afterburner           64    1448    1492    2940    352   252
bson/jackson/databind                  64    5376    6812   12188    506   286
xml/xstream+c                          64    6476   13505   19981    487   244
xml/jackson/databind-aalto             63    3001    5516    8517    683   286

DEFAULT:
Serializes arbitrary object graphs, cycle detection enabled. Nothing is known in advance about the classes to serialize. Only serializers supporting full object graph serialization are included.

                                   create     ser   deser   total   size  +dfl
java-built-in-serializer               64    5723   29259   34982    889   514
hessian                                64    3611    6169    9780    501   313
kryo-serializer                        64    1711    1499    3210    311   198
fast-serialization-shared              64    1621    1592    3212    341   212
jboss-serialization                    64    6442    6339   12781    932   582

SIMPLE/SPECIFC:
Serializes only specific classes using code generation or other special knowledge about the class.

                                   create     ser   deser   total   size  +dfl
kryo-opt                               64     658     864    1522    209   129
wobly                                  43     886     536    1422    251   151
wobly-compact                          43     903     569    1471    225   139
protobuf                              130    1225     701    1926    239   149
protostuff                             82     488     678    1166    239   150
protobuf/protostuff                    83     598     692    1290    239   149
thrift                                126    1796     795    2591    349   197
thrift-compact                        126    1555     963    2518    240   148
avro                                   89    1616    1415    3031    221   133
json/json-lib-databind                 63   26330  103150  129479    485   263
json/jsonij-jpath                      63   38015   12325   50339    478   259

MANUAL:
Serializes only specific classes using hand written serialization code.

                                   create     ser   deser   total   size  +dfl
java-manual                            63     847     632    1480    255   147
kryo-manual                            63     555     616    1171    211   131
protostuff-manual                      63     465     711    1176    239   150
avro-generic                          379    1822    1125    2947    221   133
json/jackson/manual                    63    1097    1539    2636    468   253
json/protostuff-manual                 63    1345    1816    3161    449   233
json/google-gson/manual                63    3696    3756    7452    468   253
json/json.simple/manual                63    6184    8059   14243    495   269
json/json-smart/manual/tree            63    5314    4088    9402    495   269
json/org.json/manual/tree              63    6989    8413   15403    485   259
json/argo-manual/tree                  63   66575   14578   81153    485   263
smile/jackson/manual                   63     939    1092    2031    341   244
bson/mongodb                           64    3422    7762   11184    495   278
xml/woodstox-manual                    63    3159    4578    7737    653   304
xml/aalto-manual                       63    2077    3093    5170    653   304
xml/xstream+c-woodstox                 63    5638   10506   16144    525   273
xml/xstream+c-aalto                    63    4893    8912   13805    525   273
xml/xstream+c-fastinfo                 63    8451    7971   16422    345   264
xml/javolution                         64    5544    8538   14082    504   263
xml/fastinfo-manual                    64    6959    5420   12379    377   284