早上好,
我正在为c ++寻找一种非常快速的二进制序列化技术。我只需要序列化对象中包含的数据(没有指针等)。我希望它能像它一样快 可能。如果它特定于x86硬件是可以接受的。
我熟悉这样做的C方法。作为一项测试,我在板凳上标记了几种技术。我发现C方法比我实现的最好的C ++方法快40%。
有关如何改进C ++方法(或执行此操作的库)的任何建议? 什么可用于内存映射文件?
由于
// c style writes
{
#pragma pack(1)
struct item
{
uint64_t off;
uint32_t size;
} data;
#pragma pack
clock_t start = clock();
FILE* fd = fopen( "test.c.dat", "wb" );
for ( long i = 0; i < tests; i++ )
{
data.off = i;
data.size = i & 0xFFFF;
fwrite( (char*) &data, sizeof(data), 1, fd );
}
fclose( fd );
clock_t stop = clock();
double d = ((double)(stop-start))/ CLOCKS_PER_SEC;
printf( "%8.3f seconds\n", d );
}
测试约1.6秒= 10000000
// c++ style ofstream writes
// define a DTO class
class test
{
public:
test(){}
uint64_t off;
uint32_t size;
friend std::ostream& operator<<( std::ostream& stream, const test& v );
};
// write to the stream
std::ostream& operator<<( std::ostream &stream, const test& v )
{
stream.write( (char*)&v.off, sizeof(v.off) );
stream.write( (char*)&v.size, sizeof(v.size) );
return stream;
}
{
test data;
clock_t start = clock();
std::ofstream out;
out.open( "test.cpp.dat", std::ios::out | std::ios::trunc | std::ios::binary );
for ( long i = 0; i < tests; i++ )
{
data.off = i;
data.size = i & 0xFFFF;
out << data;
}
out.close();
clock_t stop = clock();
double d = ((double)(stop-start))/ CLOCKS_PER_SEC;
printf( "%8.3f seconds\n", d );
}
测试约2.6秒= 10000000
答案 0 :(得分:14)
只有非常几个真实案例才重要。您只需序列化以使您的对象与某种外部资源兼容。磁盘,网络等。在资源上传输序列化数据的代码总是比序列化对象所需的代码慢几个数量级。如果您将序列化代码的速度提高了两倍,那么您的整体操作速度不会超过0.5%,即可。这既不是风险也不是努力。
测量三次,切一次。
答案 1 :(得分:5)
如果要执行的任务是真正的序列化,您可以查看Google的Protocol Buffers。它们提供了C ++类的快速序列化。该网站还提到了一些替代库,例如boost.serialization(当然,只表示协议缓冲区在大多数情况下都优于它们;-)
答案 2 :(得分:3)
C++ Middleware Writer是序列化库的在线替代品。 在某些情况下,它是faster than the serialization library in Boost.
答案 3 :(得分:2)
好吧,如果您希望尽可能快地进行序列化,那么您可以编写自己的序列化类并为其提供序列化每种POD类型的方法。
你带来的安全性越低,它运行得越快,调试就越困难,但是只有固定数量的内置,所以你可以枚举它们。
class Buffer
{
public:
inline Buffer& operator<<(int i); // etc...
private:
std::deque<unsigned char> mData;
};
我必须承认我不明白你的问题:
序列化可能有更好的方法。
答案 4 :(得分:2)
google flatbuffers,类似于协议缓冲但速度更快
答案 5 :(得分:1)
有什么方法可以利用保持不变的东西吗?
我的意思是,你只是尽可能快地尝试“test.c.dat”,对吧?您是否可以利用文件在序列化尝试之间不会更改的事实?如果您尝试一遍又一遍地序列化同一个文件,则可以根据此进行优化。我可以让第一次序列化尝试花费与你相同的时间,加上一点点额外的检查,然后如果你尝试再次在同一输入上运行序列化,我可以使我的第二次运行比第一次。
我知道这可能只是一个精心设计的例子,但你似乎专注于让语言尽快完成你的任务,而不是问“我需要再次完成这个吗?”这种方法的背景是什么?
我希望这有用。
-Brian J. Stinar -
答案 6 :(得分:1)
如果您使用的是Unix系统,则文件上的mmap
方式可以执行您想要的操作。
有关Windows的等效内容,请参阅http://msdn.microsoft.com/en-us/library/aa366556(VS.85).aspx。
答案 7 :(得分:1)
许多性能将依赖于内存缓冲区以及在写入磁盘之前如何填充内存块。并且有一些技巧可以使标准c ++流更快一些,例如std::ios_base::sync_with_stdio (false);
但是恕我直言,世界上不需要另外实现序列化。以下是其他人认为你可能想要研究的内容:
答案 8 :(得分:1)
由于I / O最有可能成为瓶颈,因此紧凑格式可能有所帮助。出于好奇,我尝试了以下编译为colf -s 16 C
的Colfer方案。
package data
type item struct {
off uint64
size uint32
}
......具有可比较的C测试:
clock_t start = clock();
data_item data;
void* buf = malloc(colfer_size_max);
FILE* fd = fopen( "test.colfer.dat", "wb" );
for ( long i = 0; i < tests; i++ )
{
data.off = i;
data.size = i & 0xFFFF;
size_t n = data_item_marshal( &data, buf );
fwrite( buf, n, 1, fd );
}
fclose( fd );
clock_t stop = clock();
尽管串行大小比原始结构转储小40%,但SSD的结果令人非常失望。
colfer took 0.520 seconds
plain took 0.320 seconds
由于生成的代码为pretty fast,因此您似乎不太可能通过序列化库赢得任何东西。
答案 9 :(得分:0)
C和C ++代码都可能由文件I / O控制(及时)。我建议在编写数据时使用内存映射文件,并将I / O缓冲留给操作系统。 Boost.Interprocess可能是另一种选择。
答案 10 :(得分:0)
要真正回答这个问题,C ++版本速度慢的原因是它调用了ostream.write
太多次,导致大量不必要的状态检查。您可以创建一个简单的缓冲区,只使用一个write
,您将看到差异。
如果您的磁盘/网络速度非常快,不会成为瓶颈,flatbuffers
capnproto
可以帮助您解决此问题。
否则,protobuf
,xxx-compact
...无论使用varint编码,都可以将这些数据序列化为原始大小的四分之一。
来自科学计算社区的HPS
对于这种高度结构化的数据来说也是一个很好的选择,并且由于其编码方案,在这种情况下可能是最快的速度和最小的消息大小。