Search code examples
protocol-buffersflatbuffers

performance flatbuffer vs protobuf


I have been told that flatbuffer'performance is better than proto,I did a test for performance on flatbuffer and protobuf,i can sure that the std vector had no influences.Althrough serialization and derialization is quick,but total spend time is more than proto. Is protobuf better than flatbuf or I made mistakes in some detail?

flatbuffer sample

    #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 sample:

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);

flatbuffer result

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

protobuf result

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

flat define

  namespace My.School;

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

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

proto define

package person;

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

message School
{
  repeated Person student = 1;
}

update...whole code:

#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

Solution

  • You probably want to take gettimeofday out of the inner loop, since it may take significant time that affects timing. If you don't want the push_back to cost anything, just make sure to reserve its size before the loop.

    For the rest of the code, the comparison is apples to oranges. For ProtoBuf, you only measure SerializeToString, whereas for this function to work, you must allocate (and deallocate) a vector of Protobuf objects. You are not measuring this time. FlatBuffer does all this work as part of serialization all in one go, it does not require intermediate data structures (apart from the vector mentioned above).

    The code also doesn't show how you create FlatBufferBuilder, giving that a good default size will make a lot of difference.