性能 flatbuffer 与 protobuf

performance flatbuffer vs protobuf

有人告诉我 flatbuffer 的性能比 proto 好,我在 flatbuffer 和 protobuf 上做了性能测试,我可以确定 std vector 没有 influences.Althrough 序列化并且反序列化很快,但是总花费时间超过 proto。是 protobuf 比 flatbuf 好还是我在一些细节上出错了?

平面缓冲区样本

    #include"person.pb.h"
#include<sys/time.h>
int main(int argc,char *argv[])
{
  if(argc<2)
  {
    printf("<usage> num\n");
    return -1; 
  }
  size_t iNum = strtoul(argv[1],NULL,10);

  person::School school;
  for(size_t i=0;i<iNum;++i)
  {
     person::Person *p = school.add_student();
     p->set_name("rockycai");
     p->set_age(i);  
  }

  timeval tbegin,tend;

  std::string buffer;

  gettimeofday(&tbegin,NULL);
  school.SerializeToString(&buffer);
  gettimeofday(&tend,NULL);

  unsigned long int uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 +
                                (tend.tv_usec - tbegin.tv_usec) / 1000;

  printf("serialize size %u cost time %lu\n",buffer.size(),uUsedMs);

  person::School school1;

  gettimeofday(&tbegin,NULL);
  school1.ParseFromString(buffer);
  gettimeofday(&tend,NULL);

  uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 +
                                (tend.tv_usec - tbegin.tv_usec) / 1000;

  printf("deserialize time %lu\n",uUsedMs); 

  return 0;
}

protbuf 示例:

timeval tbegin,tend;
  gettimeofday(&tbegin,NULL);

  timeval tbegin1,tend1;
  unsigned long int iTotal = 0;
  for(size_t i=0;i<iNum;++i)
  {
     auto name = builder.CreateString("rockycai"); 
     uint32_t age = i;

     auto student = CreateStudent(builder,name,age);

     gettimeofday(&tbegin1,NULL);
     student_vector.push_back(student);
     gettimeofday(&tend1,NULL);

     iTotal += ((tend1.tv_sec - tbegin1.tv_sec) * 1000 + (tend1.tv_usec - tbegin1.tv_usec) / 1000);
  }
  gettimeofday(&tend,NULL);

  unsigned long int uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 +
                                (tend.tv_usec - tbegin.tv_usec) / 1000;

  printf("insert time %lu vector time %lu\n",uUsedMs,iTotal);

  gettimeofday(&tbegin,NULL);
  auto vecStu = builder.CreateVector(student_vector);
  auto school = CreateSchool(builder,vecStu);
  gettimeofday(&tend,NULL);

  uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 + (tend.tv_usec - tbegin.tv_usec) / 1000;
  printf("flat prepare time %lu\n",uUsedMs);

  gettimeofday(&tbegin,NULL);
  builder.Finish(school);
  gettimeofday(&tend,NULL);

  uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 +
                                (tend.tv_usec - tbegin.tv_usec) / 1000;

  printf("serialized size %u costtime %lu\n",builder.GetSize(),uUsedMs);


  gettimeofday(&tbegin,NULL);
  auto res = GetSchool(builder.GetBufferPointer());
  gettimeofday(&tend,NULL);

  uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 + (tend.tv_usec - tbegin.tv_usec) / 1000;
  printf("deserialize time %lu\n",uUsedMs);

平面缓冲区结果

 $ ./sample_person 10000000
insert time 9232 vector time 1
flat prepare time 1493
serialized size 320000024 costtime 0
deserialize time 0

protobuf 结果

$ ./test_person 10000000                                                                            
serialize size 167886336 cost time 2799
deserialize time 4446

平面定义

  namespace My.School;

table Student
{
  name:string;
  age:uint32;
}

table School
{
  student:[Student];
}
root_type School;

原型定义

package person;

message Person
{
  optional string name = 1;
  optional uint32 age = 2;
}

message School
{
  repeated Person student = 1;
}

更新...完整代码:

#include "person_generated.h"
#include<sys/time.h>
using namespace My::School;

int main(int argc,char *argv[])
{
  timeval ttbegin,ttend;
  gettimeofday(&ttbegin,NULL);

  if(argc<2)
  {
     printf("<usage> num\n");
     return -1;
  }

  size_t iNum = strtoul(argv[1],NULL,10);
  flatbuffers::FlatBufferBuilder builder(10000000);
  std::vector<flatbuffers::Offset<Student>> student_vector;
  student_vector.reserve(10000000);

  timeval tbegin,tend;
  gettimeofday(&tbegin,NULL);

  //timeval tbegin1,tend1;
  unsigned long int iTotal = 0;
  for(size_t i=0;i<iNum;++i)
  {
     auto name = builder.CreateString("rockycai");
     uint32_t age = i;

     auto student = CreateStudent(builder,name,age);

     //gettimeofday(&tbegin1,NULL);
     student_vector.push_back(student);
     //gettimeofday(&tend1,NULL);

     //iTotal += ((tend1.tv_sec - tbegin1.tv_sec) * 1000 + (tend1.tv_usec - tbegin1.tv_usec) / 1000);
  }

  gettimeofday(&tend,NULL);

  unsigned long int uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 +
                                (tend.tv_usec - tbegin.tv_usec) / 1000;

  printf("insert time %lu\n",uUsedMs);

  gettimeofday(&tbegin,NULL);

  auto vecStu = builder.CreateVector(student_vector);
  auto school = CreateSchool(builder,vecStu);

  gettimeofday(&tend,NULL);
    uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 + (tend.tv_usec - tbegin.tv_usec) / 1000;
  printf("flat prepare time %lu\n",uUsedMs);

  gettimeofday(&tbegin,NULL);
  builder.Finish(school);

  gettimeofday(&tend,NULL);

  uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 +
                                (tend.tv_usec - tbegin.tv_usec) / 1000;

  printf("serialized size %u costtime %lu\n",builder.GetSize(),uUsedMs);


  gettimeofday(&tbegin,NULL);
  auto res = GetSchool(builder.GetBufferPointer());

  gettimeofday(&tend,NULL);

  uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 + (tend.tv_usec - tbegin.tv_usec) / 1000;
  printf("deserialize time %lu\n",uUsedMs);

  gettimeofday(&ttend,NULL);

  uUsedMs = (ttend.tv_sec - ttbegin.tv_sec) * 1000 + (ttend.tv_usec - ttbegin.tv_usec) / 1000;
  printf("total time %lu\n",uUsedMs);
  return 0;
}




 #include"person.pb.h"
#include<sys/time.h>
int main(int argc,char *argv[])
{

  timeval ttbegin,ttend;
  gettimeofday(&ttbegin,NULL);

  if(argc<2)
  {
    printf("<usage> num\n");
    return -1; 
  }
  size_t iNum = strtoul(argv[1],NULL,10);

  person::School school;

  timeval tbegin,tend;
  gettimeofday(&tbegin,NULL);
  for(size_t i=0;i<iNum;++i)
  {
     person::Person *p = school.add_student();
     p->set_name("rockycai");
     p->set_age(i);  
  }

  gettimeofday(&tend,NULL);  

  unsigned long int uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 + (tend.tv_usec - tbegin.tv_usec) / 1000;

  printf("vector time %lu\n",uUsedMs);
  std::string buffer;

  gettimeofday(&tbegin,NULL);
  school.SerializeToString(&buffer);
  gettimeofday(&tend,NULL);

  uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 +
                                (tend.tv_usec - tbegin.tv_usec) / 1000;

  printf("serialize size %u cost time %lu\n",buffer.size(),uUsedMs);

  person::School school1;

  gettimeofday(&tbegin,NULL);
  school1.ParseFromString(buffer);
  gettimeofday(&tend,NULL);

  uUsedMs = (tend.tv_sec - tbegin.tv_sec) * 1000 +
                                (tend.tv_usec - tbegin.tv_usec) / 1000;

  printf("deserialize time %lu\n",uUsedMs); 

    gettimeofday(&ttend,NULL);
  uUsedMs = (ttend.tv_sec - ttbegin.tv_sec) * 1000 + (ttend.tv_usec - ttbegin.tv_usec) / 1000;
  printf("total time %lu\n",uUsedMs);

  return 0;
}

res is:



 $ ./sample_person 10000000
insert time 7763
flat prepare time 1159
serialized size 320000024 costtime 0
deserialize time 0
total time 8922




  $ ./test_person 10000000
vector time 2598
serialize size 167886336 cost time 2706
deserialize time 4676
total time 9981

您可能希望将 gettimeofday 排除在内部循环之外,因为这可能会花费大量时间来影响计时。如果您不希望 push_back 花费任何费用,请确保在循环之前保留其大小。

对于代码的其余部分,比较是从苹果到橘子。对于 ProtoBuf,您只需测量 SerializeToString,而要使此功能起作用,您必须分配(和取消分配)Protobuf 对象的向量。你不是在测量这个时间。 FlatBuffer 将所有这些工作作为序列化的一部分一次性完成,它不需要中间数据结构(除了上面提到的向量)。

该代码也没有显示您如何创建 FlatBufferBuilder,考虑到良好的默认大小会产生很大的不同。