性能 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
,考虑到良好的默认大小会产生很大的不同。
有人告诉我 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
,考虑到良好的默认大小会产生很大的不同。