对比ProtoBuf和JSON的序列化和反序列化能力

1.序列化能力对比验证

在这里让我们分别使用PB与JSON的序列化与反序列化能力,对值完全相同的一份结构化数据进行不同次数的性能测试。
为了可读性,下面这一份文本使用JSON格式展示了需要被进行测试的结构化数据内容:

{
    "age" : 20,
    "name" : "张珊",
    "phone" :
    [
    {
        "number" : "110112119",
        "type" : 0
    },
    {
        "number" : "110112119",
        "type" : 0
    },
    {
        "number" : "110112119",
        "type" : 0
    },
    {
        "number" : "110112119",
        "type" : 0
    },
    {
        "number" : "110112119",
        "type" : 0
    }
    ],
    "qq" : "95991122",
    "address" :
    {
        "home_address" : "陕西省西安市⻓安区",
        "unit_address" : "陕西省西安市雁塔区"
    },
    "remark" :
    {
        "key1" : "value1",
        "key2" : "value2",
        "key3" : "value3",
        "key4" : "value4",
        "key5" : "value5"
    }
}

开始进行测试代码编写,我们在新的目录下新建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的应用场景更为丰富。

  • 36
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 32
    评论
ProtoBuf是一种数据序列化反序列化的技术,它的全称是Protocol Buffers。ProtoBuf可以将结构化数据转换为二进制格式,以便在不同的系统之间进行高效的数据交换。使用ProtoBuf进行序列化反序列化有以下几个优势: 1. 空间效率:ProtoBuf使用紧凑的二进制编码格式,可以大大减少数据的存储空间。相比于传统的文本格式如XML和JSONProtoBuf的数据大小通常要小得多。 2. 时间效率:ProtoBuf的解析和生成代码都是使用高效的机器生成,因此在序列化反序列化过程中可以获得更快的速度。特别是在处理大规模数据时,ProtoBuf的效率更加显著。 3. 可读性:虽然ProtoBuf的数据是以二进制形式存储的,但是它的定义文件是以文本形式进行编写的,具有良好的可读性和可维护性。开发人员可以通过阅读ProtoBuf定义文件了解数据结构的字段和类型。 在前后端交互中,ProtoBuf可以作为一种通用的数据交换格式。前端可以将数据序列化ProtoBuf格式,然后发送给后端进行处理。后端可以将接收到的ProtoBuf数据反序列化为对象,然后进行相应的业务逻辑处理。 下面是使用ProtoBuf进行序列化反序列化的示例代码: ```java // 创建一个user对象 User user = User.builder().id("1").age(20).name("张三").desc("programmer").build(); // 创建一个Group对象,将user放入group中 Group group = Group.builder().id("1").name("分组1").user(user).build(); // 使用ProtostuffUtils序列化 byte[] data = ProtostuffUtils.serialize(group); System.out.println("序列化后:" + Arrays.toString(data)); // 使用ProtostuffUtils反序列化 Group result = ProtostuffUtils.deserialize(data, Group.class); System.out.println("反序列化后:" + result.toString()); ``` 在上述代码中,首先创建了一个User对象和一个Group对象,然后使用ProtostuffUtils对Group对象进行序列化,将其转换为字节数组。接着使用ProtostuffUtils对字节数组进行反序列化,将其转换为Group对象。最后输出反序列化后的Group对象。 引用:
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linkindly

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值