最快的c ++序列化?

问题描述 投票:17回答:11

大家早上好,

我正在为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

c++ serialization
11个回答
14
投票

现实案例中很少有重要事项。您只需序列化以使您的对象与某种外部资源兼容。磁盘,网络等。在资源上传输序列化数据的代码总是比序列化对象所需的代码慢几个数量级。如果您将序列化代码的速度提高了两倍,那么您的整体操作速度不会超过0.5%,即可。这既不是风险也不是努力。

测量三次,切一次。


0
投票

C和C ++代码都可能由文件I / O控制(及时)。我建议在编写数据时使用内存映射文件,并将I / O缓冲留给操作系统。 Boost.Interprocess可能是另一种选择。


0
投票

要真正回答这个问题,C ++版本速度慢的原因是它调用了ostream.write太多次,导致大量不必要的状态检查。你可以创建一个简单的缓冲区,只使用一个write,你会看到差异。

如果您的磁盘/网络真的足够快,不会成为瓶颈,flatbuffers capnproto是您处理此问题的绝佳选择。

否则,protobufxxx-compact ......无论使用varint编码,都可以将这些数据序列化为原始大小的四分之一。来自科学计算社区的HPS对于这种高度结构化的数据也是一个很好的选择,并且由于其编码方案,在这种情况下可能是速度最快和消息大小最小的。


5
投票

如果要执行的任务是真正的序列化,您可以查看Google的Protocol Buffers。它们提供了C ++类的快速序列化。该网站还提到了一些替代库,例如boost.serialization(当然,只表示协议缓冲区在大多数情况下都胜过它们;-)


3
投票

C++ Middleware Writer是序列化库的在线替代品。在某些情况下,它是faster than the serialization library in Boost.


2
投票

好吧,如果您希望尽可能快地进行序列化,那么您可以编写自己的序列化类并为其提供序列化每种POD类型的方法。

你带来的安全性越低,它运行得越快,调试就越困难,但是只有固定数量的内置,所以你可以枚举它们。

class Buffer
{
public:
  inline Buffer& operator<<(int i); // etc...
private:
  std::deque<unsigned char> mData;
};

我必须承认我不明白你的问题:

  • 你真的想对序列化的消息做什么?
  • 你以后要保存吗?
  • 你是否担心前向/后向兼容性?

序列化可能有更好的方法。


2
投票

google flatbuffers,类似于协议缓冲区但速度更快

https://google.github.io/flatbuffers/

https://google.github.io/flatbuffers/md__benchmarks.html


1
投票

有什么方法可以利用保持不变的东西吗?

我的意思是,你只是试图尽可能快地运行“test.c.dat”,对吧?您是否可以利用文件在序列化尝试之间不会更改的事实?如果您尝试一遍又一遍地序列化同一个文件,则可以根据此进行优化。我可以让第一次序列化尝试花费与你相同的时间,加上一点点额外的检查,然后如果你尝试再次在同一输入上运行序列化,我可以使我的第二次运行比第一次。

我知道这可能只是一个精心设计的例子,但你似乎专注于让语言尽快完成你的任务,而不是问“我需要再次完成这个吗?”这种方法的背景是什么?

我希望这是有帮助的。

-Brian J. Stinar-


1
投票

如果您使用的是Unix系统,那么文件中的mmap就是您想要做的事情。

有关Windows的等效内容,请参阅http://msdn.microsoft.com/en-us/library/aa366556(VS.85).aspx


1
投票

很多性能将取决于内存缓冲区以及在写入磁盘之前如何填充内存块。还有一些技巧可以让标准c ++流更快一些,比如std::ios_base::sync_with_stdio (false);

但恕我直言,世界上不需要另外实现序列化。以下是其他人认为你可能想要研究的内容:

  • Boost:快速,多样的C ++库,包括序列化
  • protobuf:使用C ++模块进行快速跨平台,跨语言的序列化
  • thrift:使用C ++模块灵活的跨平台,跨语言序列化

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,因此您似乎不太可能通过序列化库赢得任何东西。

© www.soinside.com 2019 - 2024. All rights reserved.