【问题标题】:Boost Deserialization Optimizations?提升反序列化优化?
【发布时间】:2009-04-11 12:49:48
【问题描述】:

我正在通过 Boost.Serialization 反序列化大量数据(每帧一个)。但是,当我输出反序列化需要多长时间时,它变化很大。目前它并不是非常慢,但让它更快会很好。数据代表相同的类、数组、映射和向量,只是具有不同的值。

看到每次反序列化发生时内存激增,我必须相信有比不断分配和释放所有这些内存更好的方法。

以下是几个读取时间的例子:

Deserialization - 390 milliseconds
Deserialization - 422 milliseconds
Deserialization - 422 milliseconds
Deserialization - 422 milliseconds
Deserialization - 438 milliseconds
Deserialization - 2156 milliseconds
Deserialization - 1797 milliseconds
Deserialization - 1656 milliseconds
Deserialization - 1328 milliseconds
Deserialization - 1219 milliseconds
Deserialization - 1078 milliseconds
Deserialization - 1078 milliseconds

有没有一种方法可以为使用 Boost.Serialization 的相同数据编写自定义反序列化函数,以便我可以指定在开始时分配内存,然后只为每一帧更改它们的值?

更新:我意识到我使用的优化标志的一个小问题是导致序列化数据写入不正确,从而导致反序列化时间不一致。修复此问题后,它现在始终保持在每帧 750 - 780 毫秒。

但是,我最初的问题仍然存在,因为目前我正在序列化和反序列化整个 stl 容器,而我真的只想序列化内容(因为容器的大小和索引将保持完全相同)。不过,我不确定执行此操作的最佳方法。

【问题讨论】:

  • 什么框架?可能是视频?此外,各个帧反序列化时间是否可重复运行?另外,您是从硬盘还是内存读取输入?如果是前者,那么硬盘寻道时间以及帧是否已被缓存可能会支配其他一切。
  • 基本上是视频帧(尽管它实际上代表数据)。请参阅我对问题的更新。关于输入,它都是从磁盘序列化和反序列化的。也许在遍历帧之前将数据从磁盘初始传输到内存会有所帮助。

标签: c++ optimization serialization boost


【解决方案1】:

您可能需要重新考虑使用此序列化的功能设计。

从您的描述看来,您似乎非常频繁地序列化/反序列化整个 STL 容器。这不应该是必需的。 Serialization 不应该被使用,除非数据需要被持久化以便以后可以重建或由其他人重建。

如果您的应用程序需要序列化,您可以考虑单独序列化容器中的每个项目,然后仅在项目更改时重新序列化。这样您就不会不必要地重复所有工作。

【讨论】:

    【解决方案2】:

    免责声明:我不使用 boost 序列化。

    话虽如此:这些问题的标准问题或多或少是您所描述的:对于任何单个序列化,您从操作系统请求几个(有时很多)内存块,然后释放它们,当您实际或多或少地知道您需要多少内存。我知道的唯一解决方案是实现类似于 Apache“池”的东西:分配一块内存并让分配器(你给 stl 或 boost)在这个池中工作;然后你有像 Pool::string 这样的类,它们在池内分配内存。两个警告:

    • 是的,这不是很 C++,但您可以在它周围加上一个漂亮的信封(我工作的系统架构师做了这样的事情)。
    • 是的,您必须注意需要分配另一个池的情况。

    【讨论】:

      【解决方案3】:

      Boost 序列化为 STL 集合提供了模板化的保存方法,例如一组:

      template<class Archive, class Key, class Compare, class Allocator >
      inline void save(
          Archive & ar,
          const std::set<Key, Compare, Allocator> &t,
          const unsigned int /* file_version */
      ){
          boost::serialization::stl::save_collection<
              Archive, std::set<Key, Compare, Allocator> 
          >(ar, t);
      }
      

      它只是委托给 save_collection。我想您可以为您的集合类型定义一个专业化,以您选择的方式进行序列化,例如:

      namespace boost {
      namespace serialization {
      
      template<class Archive>
      inline void save<MyKey, MyCompare, MyAlloc> (
          Archive & ar,
          const std::set<MyKey, MyCompare, MyAlloc> &t,
          const unsigned int /* file_version */
      ){
          // ...
      }
      
      }
      }
      

      您可以将 save_collection 实现的副本(来自 collections_save_imp.hpp)作为起点,并对其进行优化以满足您的要求。例如。使用一个能记住上次调用的集合大小的类,如果它没有改变,则重新使用相同的缓冲区。

      您可能还需要针对您的会员类型进行专业化。在这一点上,您是否从 boost 序列化中获得任何价值是值得怀疑的。

      我知道这有点含糊,但如果不知道您使用的是什么集合类型、成员类型是什么等,就很难更具体。

      【讨论】:

        【解决方案4】:

        由于我看不到您的代码,我的第一个建议是使用分析工具来确定实际的瓶颈。

        内存分配/解除分配很可能是核心问题,在这种情况下,池分配 - 假设您有固定大小的对象 - 正如 David 提到的那样。可能会有所帮助。

        如果您将大量对象序列化为常规 ASCII 格式文件,则从 ASCII 到二进制的转换很可能会耗费一些不小的时间。 atoi() 的等价物可能很快,但是当你说“帧”时,我假设某种图片或网络缓冲区,它可能很大/很长。因此, atoi() 的开销在执行数百万次后可能会非常显着。

        我也不明白你对内容和对象所做的区分。一般来说,集合在空间方面没有太多开销,在散列和树重新平衡方面可能有一些“时间”开销,但分析器也应该显示这一点。

        简而言之,您认为速度较慢的部分可能对整体性能的影响有限。配置文件并确定查看。

        【讨论】:

          猜你喜欢
          • 2018-12-22
          • 2021-12-03
          • 1970-01-01
          • 1970-01-01
          • 2021-05-25
          • 2012-11-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多