【问题标题】:Implement tree structure. Store nodes in vector and denote children with indices? Or store nodes on the heap and use pointers to the children?实现树形结构。将节点存储在向量中并用索引表示子节点?或者将节点存储在堆上并使用指向子节点的指针?
【发布时间】:2021-07-02 01:30:06
【问题描述】:

我必须在 C++ 中实现一个树形结构(确切地说是一个 trie)。它处于热门路径,并且可能会变得非常大(很难给出期望,可能大约 1mio 节点)。
因此,我目前正在考虑如何尽可能高效地实现该结构。我有两个想法:

  1. 第一个相当直接:分配堆上的所有节点,管理指针并通过指向堆上对象的指针表示节点的子节点。但是,我担心重复分配(新的调用)会很慢。当然,使用原始指针总是有点危险和麻烦。

  2. 第二个想法是将所有节点存储在一个池中。我会使用一个包含所有节点的向量。节点的子节点将由该向量的一组索引表示。因此,我们可以在程序开始时保留 1mio 节点,并且可以说几乎没有调整大小。但是,当发生调整大小时,它们显然会非常昂贵。

您建议如何解决这个问题?哪些缺点更重?

提前感谢!

【问题讨论】:

  • 是否删除了某个节点?如果是,那之后是否分配了额外的节点?更一般地说,数据结构的典型工作负载/用法是什么?数据结构是否在并行上下文中使用?
  • 没有删除节点。仅在非常不规则的基础上添加节点。也就是说,可能有数百个级别的分支,但每个级别仅分支到 1 或子级。另一方面,它也可能在前几层有数千个分支,然后才逐渐变薄。编辑:没有并行contxt。
  • 32 位索引提供的信息密度比 64 位指针高约 2 倍。如果你真的想推送它,你可以在char[3] 中使用 24 位索引(最多 16,777,215 个节点),但是你必须非常小心,否则你很容易因为填充而意外丢失节省的空间。

标签: c++ performance pointers memory


【解决方案1】:

标准的高效 C++ 方法是编写数据结构而不用担心数据如何分配,并使用专用的分配器模板参数(如std::vectorstd::map)。这种方法有助于保持您的代码可维护(感谢关注点分离)并且仍然非常高效(感谢模板)。使用此解决方案,您可以先轻松编写代码(使用方法 n°1),然后也可以轻松优化分配(例如使用方法 n°2)。

关于分配的优化:是的,newdelete 可能会很慢(尽管有些平台这样做非常有效)。 由于没有删除节点,所以可以使用非常快的monotonic arena/stack-based allocator (您可以查看似乎可以做到的std::pmr::monotonic_buffer_resource)。这个想法是在内存块中堆叠小的分配内存区域。可以一个一个地预先分配或动态分配块(可能使用不断增长的策略,但无需调整数据大小)。块指针可以存储在一个非常小的数据结构中(可能是固定大小的数组)。当你的数据结构被删除时,你只需要删除几个块(你甚至可以循环回收它们)。这种策略应该比第二种方法便宜,因为调整向量的大小非常昂贵。更多信息,您可以查看CppCon 2017 talk of John Lakos on Local ('Arena') Memory Allocators

【讨论】:

    猜你喜欢
    • 2020-03-07
    • 1970-01-01
    • 2020-01-15
    • 2021-02-19
    • 2018-04-07
    • 2018-09-20
    • 1970-01-01
    • 2015-06-15
    • 2013-05-21
    相关资源
    最近更新 更多