1.序列化能力对比验证
在这里让我们分别使用PB与JSON的序列化与反序列化能力,对值完全相同的一份结构化数据进行不同次数的性能测试。
为了可读性,下面这一份文本使用JSON格式展示了需要被进行测试的结构化数据内容:
开始进行测试代码编写,我们在新的目录下新建contacts. proto文件,内容如下:
-
syntax="proto3";
-
package compare_serialization;
-
import "google/protobuf/any.proto"; // 引⼊ any.proto ⽂件
-
// 地址
-
message Address{
-
string home_address = 1; // 家庭地址
-
string unit_address = 2; // 单位地址
-
}
-
// 联系⼈
-
message PeopleInfo {
-
string name = 1; // 姓名
-
int32 age = 2; // 年龄
-
message Phone {
-
string number = 1; // 电话号码
-
enum PhoneType {
-
MP = 0; // 移动电话
-
TEL = 1; // 固定电话
-
}
-
PhoneType type = 2; // 类型
-
}
-
repeated Phone phone = 3; // 电话
-
google.protobuf.Any data = 4;
-
oneof other_contact { // 其他联系⽅式:多选⼀
-
string qq = 5;
-
string weixin = 6;
-
}
-
map<string, string> remark = 7; // 备注
-
}
使用protoc命令编译文件后,新建性能测试文件compare.cc,我们分别对相同的结构化数据进行
100、1000、 10000 、100000 次的序列化与反序列化,分别获取其耗时与序列化后的大小。
内容如下:
-
#include <iostream>
-
#include <sys/time.h>
-
#include <jsoncpp/json/json.h>
-
#include "contacts.pb.h"
-
using namespace std;
-
using namespace compare_serialization;
-
using namespace google::protobuf;
-
#define TEST_COUNT 100000
-
void createPeopleInfoFromPb(PeopleInfo *people_info_ptr);
-
void createPeopleInfoFromJson(Json::Value& root);
-
int main(int argc, char *argv[])
-
{
-
struct timeval t_start,t_end;
-
double time_used;
-
int count;
-
string pb_str, json_str;
-
// ------------------------------Protobuf 序列化------------------------------------
-
{
-
PeopleInfo pb_people;
-
createPeopleInfoFromPb(&pb_people);
-
count = TEST_COUNT;
-
gettimeofday(&t_start, NULL);
-
// 序列化count次
-
while ((count--) > 0) {
-
pb_people.SerializeToString(&pb_str);
-
}
-
gettimeofday(&t_end, NULL);
-
time_used=1000000*(t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec - t_start.tv_usec;
-
cout << TEST_COUNT << "次 [pb序列化]耗时:" << time_used/1000 << "ms."
-
<< " 序列化后的大小:" << pb_str.length() << endl;
-
}
-
// ------------------------------Protobuf 反序列化------------------------------------
-
{
-
PeopleInfo pb_people;
-
count = TEST_COUNT;
-
gettimeofday(&t_start, NULL);
-
// 反序列化count次
-
while ((count--) > 0) {
-
pb_people.ParseFromString(pb_str);
-
}
-
gettimeofday(&t_end, NULL);
-
time_used=1000000*(t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec - t_start.tv_usec;
-
cout << TEST_COUNT << "次 [pb反序列化]耗时:" << time_used / 1000 << "ms." << endl;
-
}
-
// ------------------------------JSON 序列化------------------------------------
-
{
-
Json::Value json_people;
-
createPeopleInfoFromJson(json_people);
-
Json::StreamWriterBuilder builder;
-
count = TEST_COUNT;
-
gettimeofday(&t_start, NULL);
-
// 序列化count次
-
while ((count--) > 0) {
-
json_str = Json::writeString(builder, json_people);
-
}
-
gettimeofday(&t_end, NULL);
-
// 打印序列化结果
-
// cout << "json: " << endl << json_str << endl;
-
time_used=1000000*(t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec - t_start.tv_usec;
-
cout << TEST_COUNT << "次 [json序列化]耗时:" << time_used/1000 << "ms."
-
<< " 序列化后的大小:" << json_str.length() << endl;
-
}
-
// ------------------------------JSON 反序列化------------------------------------
-
{
-
Json::CharReaderBuilder builder;
-
unique_ptr<Json::CharReader> reader(builder.newCharReader());
-
Json::Value json_people;
-
count = TEST_COUNT;
-
gettimeofday(&t_start, NULL);
-
// 反序列化count次
-
while ((count--) > 0) {
-
reader->parse(json_str.c_str(), json_str.c_str() + json_str.length(), &json_people, nullptr);
-
}
-
gettimeofday(&t_end, NULL);
-
time_used=1000000*(t_end.tv_sec - t_start.tv_sec) + t_end.tv_usec - t_start.tv_usec;
-
cout << TEST_COUNT << "次 [json反序列化]耗时:" << time_used/1000 << "ms." << endl;
-
}
-
return 0;
-
}
-
/**
-
* 构造pb对象
-
*/
-
void createPeopleInfoFromPb(PeopleInfo *people_info_ptr)
-
{
-
people_info_ptr->set_name("张珊");
-
people_info_ptr->set_age(20);
-
people_info_ptr->set_qq("95991122");
-
for(int i = 0; i < 5; i++) {
-
PeopleInfo_Phone* phone = people_info_ptr->add_phone();
-
phone->set_number("110112119");
-
phone->set_type(PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MP);
-
}
-
Address address;
-
address.set_home_address("陕西省西安市长安区");
-
address.set_unit_address("陕西省西安市雁塔区");
-
google::protobuf::Any * data = people_info_ptr->mutable_data();
-
data->PackFrom(address);
-
people_info_ptr->mutable_remark()->insert({"key1", "value1"});
-
people_info_ptr->mutable_remark()->insert({"key2", "value2"});
-
people_info_ptr->mutable_remark()->insert({"key3", "value3"});
-
people_info_ptr->mutable_remark()->insert({"key4", "value4"});
-
people_info_ptr->mutable_remark()->insert({"key5", "value5"});
-
}
-
/**
-
* 构造json对象
-
*/
-
void createPeopleInfoFromJson(Json::Value& root) {
-
root["name"] = "张珊";
-
root["age"] = 20;
-
root["qq"] = "95991122";
-
for(int i = 0; i < 5; i++) {
-
Json::Value phone;
-
phone["number"] = "110112119";
-
phone["type"] = 0;
-
root["phone"].append(phone);
-
}
-
Json::Value address;
-
address["home_address"] = "陕西省西安市长安区";
-
address["unit_address"] = "陕西省西安市雁塔区";
-
root["address"] = address;
-
Json::Value remark;
-
remark["key1"] = "value1";
-
remark["key2"] = "value2";
-
remark["key3"] = "value3";
-
remark["key4"] = "value4";
-
remark["key5"] = "value5";
-
root["remark"] = remark;
-
}
makefile:
-
compare:*.cc
-
g++ -o $@ $^ -std=c++11 -lprotobuf
-
.PHONY:clean
-
clean:
-
rm -rf compare
测试结果如下:
100次 [pb序列化]耗时:0.382ms. 序列化后的大小:278
100次 [pb反序列化]耗时:0.442ms.
100次 [json序列化]耗时:2.43ms. 序列化后的大小:567
100次 [json反序列化]耗时:1.091ms.1000次 [pb序列化]耗时:3.196ms. 序列化后的大小:278
1000次 [pb反序列化]耗时:5.047ms.
1000次 [json序列化]耗时:20.22ms. 序列化后的大小:567
1000次 [json反序列化]耗时:13.037ms.10000次 [pb序列化]耗时:29.206ms. 序列化后的大小:278
10000次 [pb反序列化]耗时:48.03ms.
10000次 [json序列化]耗时:206.259ms. 序列化后的大小:567
10000次 [json反序列化]耗时:114.738ms.
由实验结果可得:
●编解码性能: ProtoBuf 的编码解码性能,比JSON高出2-4倍。
●内存占用: ProtoBuf的内存278,而JSON到达567, ProtoBuf的内存占用只有JSON的1/2。
注:以上结论的数据只是根据该项实验得出。因为受不同的字段类型、字段个数等影响,测出的数据会有所差异。
2.总结
小结:
1. XML、JSON、 ProtoBuf 都具有数据结构化和数据序列化的能力。
2. XML、JSON更注重数据结构化,关注可读性和语义表达能力。ProtoBuf 更注重数据序列化,关注效率、空间、速度,可读性差,语义表达能力不足,为保证极致的效率,会舍弃一部分元信息。
3. ProtoBuf 的应用场景更为明确,XML、JSON的应用场景更为丰富。