【问题标题】:Remove 100,000+ nodes from a Boost graph从 Boost 图中删除 100,000 多个节点
【发布时间】:2015-02-06 23:07:03
【问题描述】:

我有一个图( adjacency_list (listS, vecS, bidirectionalS, VertexVal) ),我需要在其中删除 100,000 多个节点。每个节点还包含一个由 2 个 64 位整数和另一个 64 位整数组成的结构。下面代码中发生的 guid 检查是检查结构中的第一个整数。

在我的笔记本电脑(i7 2.7GHz,16GB RAM)上,根据 VTune 大约需要 88 秒。

以下是我删除节点的方法:

  vertex_iterator vi,vi_end;
  boost::tie(vi, vi_end) = boost::vertices(m_graph);
  while (vi!=vi_end) {
    if (m_graph[*vi].guid.part1 == 0) {
      boost::remove_vertex(*vi,m_graph);
      boost::tie(vi, vi_end) = boost::vertices(m_graph);
    } else 
      ++vi;
  }

Vtune 显示 boost::remove_vertex() 调用需要 88.145 秒。有没有更有效的方法来删除这些顶点?

【问题讨论】:

  • 并且图类型是...(您应该指定相关信息。(捆绑的)属性,容器选择,它们会产生巨大的影响)
  • adjacency_list with (listS, vecS, bidirectionalS, VertexVal.)
  • 每个节点还包含一个顶点值,它是一个包含 3 个 64 位整数的结构。
  • guid 是一个包含 2 个 64 位整数的结构 - 我上面提到的 2/3 个整数。我将编辑问题 - 问题已解决:)

标签: c++ boost


【解决方案1】:

在您的删除分支中,您重新tie() 迭代器:

boost::tie(vi, vi_end) = boost::vertices(m_graph);

这将导致每次重新启动循环时都重新启动循环。这是完全正确Schlemiel The Painter。

我会查明您是否可以信任 remove_vertex 不会触发重新分配。如果是这样,它很容易修复。否则,您需要基于索引器的循环而不是基于迭代器的循环。或者您可以使用原始容器(不过,我记得它是一个私有成员)。

更新使用vecS作为顶点容器会导致性能不佳:

如果adjacency_list 的VertexList 模板参数为vecS,则该操作会使图的所有顶点描述符、边描述符和迭代器失效。 <...>如果您需要频繁使用remove_vertex() 函数,listS 选择器是VertexList 模板参数的更好选择。


这个small benchmark test.cpp比较:

  • 与-DSTABLE_IT (listS)

    $ ./stable 
    Generated 100000 vertices and 5000 edges in 14954ms
    The graph has a cycle? false
    starting selective removal...
    Done in 0ms
    After: 99032 vertices and 4916 edges
    
  • 没有-DSTABLE_IT (vecS)

    $ ./unstable 
    Generated 100000 vertices and 5000 edges in 76ms
    The graph has a cycle? false
    starting selective removal...
    Done in 396ms
    After: 99032 vertices and 4916 edges
    
  • 使用 filtered_graph(感谢 cmets 中的 @cv_and_he)

    Generated 100000 vertices and 5000 edges in 15ms
    The graph has a cycle? false
    starting selective removal...
    Done in 0ms
    After: 99032 vertices and 4916 edges
    Done in 13ms
    

您可以清楚地看到,listS 的删除速度多快,但生成速度多慢。

【讨论】:

  • 之所以必须这样做是因为当remove_vertex被调用时,所有的迭代器都失效了。
  • vecS 必须使用,因为我必须检测循环依赖关系。 boost example 这仅适用于 vecS
  • @Dula 用于添加顶点的大型图会慢得多,但我对随机图的测试已经证实 listS 是 much在删除阶段更快。一般来说,如果你证明了vertex_index_t 属性映射,那么与vecS“自然”工作的算法仍然适用于listS。我稍后看看
  • 我认为在我的用例中较慢的生成是可以的。只有删除是批量发生的。我将尝试修改图表以使用 listS 并发布结果数据。
  • @Dula 我记得读过一个从带有向量 vertex_list 的图中“删除”大量顶点的好方法是创建一个过滤图,它忽略集合中的顶点,然后简单地添加顶点你想删除到那个集合。 Here 是我实现它的尝试(请务必检查哈希,因为我对 unordered_sets 知之甚少,而且很可能是错误的)。删除部分显然很快,但我不确定访问filtered_graph之后的性能如何(可能要慢得多)。
【解决方案2】:

我能够使用 Boost 序列化例程成功地将图形序列化为字符串,解析字符串并删除我不需要的节点并反序列化修改后的字符串。对于图中总共 200,000 个节点和需要删除的 100,000 个节点,我能够在不到 2 秒的时间内成功完成操作。

对于我的特定用例,每个顶点都有 3 个 64 位整数。当需要删除时,我将其中的 2 个整数标记为 0。一个有效的顶点永远不会有 0。当要清理图形时——删除“已删除”的顶点,我遵循上述逻辑。

在下面的代码中 removeDeletedNodes() 执行字符串解析和删除顶点并映射边缘编号。

【讨论】:

  • 不错。我很想知道filtered_graph 是否可以序列化(我想可以)。在这种情况下,由于减少了对字符串操作的需求,它可能会更快。
【解决方案3】:

看到更多的 Vtune 数据会很有趣。

我的经验是,在删除数以万计的小对象时,默认的 Microsoft 分配器可能是一个很大的瓶颈。您的 Vtune 图表是否显示大量删除或空闲时间?

如果是这样,请考虑切换到第三方分配器。 Nedmalloc据说不错:http://www.nedprod.com/programs/portable/nedmalloc/

Google 有一个 tcmalloc,它备受推崇,比几乎所有平台上的内置分配器都要快得多。 https://code.google.com/p/gperftools/ tcmalloc 不是 Windows 的插件。

【讨论】:

  • 是的,确实如此。在 VTune 图片中,擦除花费了 48 秒。我猜这也会导致 GUID 结构被删除?
  • erase 将覆盖析构函数以及释放内存。你能再深入一点吗?
  • 我不这么认为。那是进入 C++ 容器代码不是吗?
  • 当然,但这通常不会停止 VTune。
猜你喜欢
  • 1970-01-01
  • 2013-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-31
  • 1970-01-01
  • 2018-11-05
  • 1970-01-01
相关资源
最近更新 更多