【问题标题】:Time complexity/performance of edge and vertex properties in Boost GraphBoost Graph 中边和顶点属性的时间复杂度/性能
【发布时间】:2022-01-22 21:46:34
【问题描述】:

考虑:

typedef adjacency_list<
    listS, //out edges stored as std::list
    listS, //verteices stored as std::list
    directedS,
    property<vertex_name_t, std::string>,
    property<edge_weight_t, double>
> user_graph;

将边和顶点存储为std::list 可防止通过[index] 进行随机访问。

进一步考虑,属性映射是这样定义的。

typedef property_map<user_graph, vertex_name_t>::type name_map_t;
typedef property_map<user_graph, edge_weight_t>::type weight_map_t;

user_graph g;
name_map_t name_map = get(vertex_name, g);
weight_map_t weight_map = get(edge_weight, g);

即使通过[index] 无法随机访问实际边/顶点,是否可以保证在随机访问下访问顶点名称和边的权重是高效且快速的,如下所示:

graph_traits<user_graph>::vertex_iterator vi, vi_end;
for(tie(vi, vi_end)=vertices(g); vi != vi_end; ++vi)
    cout<<"Name of vertex is "<<name_map[*vi];//Question is, is this efficient given storage of vertices as std::list

我的部分困惑来自一般的std::map 特性,即它也不支持随机访问(参见here

无论顶点存储为std::vectorstd::liststd::set,是否无论如何,通过使用some_map[vertex_descriptor]some_map[*vertex_iterator]的顶点描述符访问其属性映射总是保证有效(恒定时间)?

【问题讨论】:

    标签: c++ boost boost-graph boost-property-map


    【解决方案1】:

    是否保证在随机访问下访问顶点的名称和边的权重是高效且快速的,如下所示:

    是的。这些属性实际上是与顶点/边节点内联存储的。描述符实际上是指向该节点的类型擦除指针。如果您将属性存储想象成一种带有get&lt;&gt; 访问器的元组,name_map[*vi] 最终会内联到 get&lt;N&gt;(*static_cast&lt;storage_node*&gt;(vi)) 之类的东西。

    我的部分困惑来自一般的 std::map 特性,它也不支持随机访问

    属性映射不像 std::map;它们可能是连续的,它们可能是基于节点的、有序的、无序的,甚至是计算的。实际上 Boost Property Map 可能更接近某些函数式编程语言中的镜头概念。它是一组函数,可用于对给定键类型的(可变)投影进行建模。

    另见:

    好奇心胜...代码生成

    让我们看看生成了什么代码:

    Live On Compiler Explorer

    #include <boost/graph/adjacency_list.hpp>
    #include <fmt/format.h>
    
    using G =
        boost::adjacency_list<boost::listS, // out edges stored as list
                            boost::listS, // vertices stored as list
                            boost::directedS,
                            boost::property<boost::vertex_name_t, std::string>,
                            boost::property<boost::edge_weight_t, double>>;
    
    using V = G::vertex_descriptor;
    using E = G::edge_descriptor;
    
    void test(V v, E e, G const& g) {
        auto name   = get(boost::vertex_name, g);
        auto weight = get(boost::edge_weight, g);
    
        fmt::print("{} {}\n", name[v], weight[e]);
    }
    
    int main()
    {
        G g;
        E e = add_edge(add_vertex({"foo"}, g), add_vertex({"bar"}, g), {42}, g).first;
    
        test(vertex(0, g), e, g);
    }
    

    打印

    foo 42
    

    但更有趣的是,codegen:

    test(void*, boost::detail::edge_desc_impl<boost::directed_tag, void*>, boost::adjacency_list<boost::listS, boost::listS, boost::directedS, boost::property<boost::vertex_name_t, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, boost::no_property>, boost::property<boost::edge_weight_t, double, boost::no_property>, boost::no_property, boost::listS> const&): # @test(void*, boost::detail::edge_desc_impl<boost::directed_tag, void*>, boost::adjacency_list<boost::listS, boost::listS, boost::directedS, boost::property<boost::vertex_name_t, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, boost::no_property>, boost::property<boost::edge_weight_t, double, boost::no_property>, boost::no_property, boost::listS> const&)
      sub rsp, 40
      mov rax, qword ptr [rsp + 64]
      movups xmm0, xmmword ptr [rdi + 24]
      mov rax, qword ptr [rax]
      movaps xmmword ptr [rsp], xmm0
      mov qword ptr [rsp + 16], rax
      mov rcx, rsp
      mov edi, offset .L.str
      mov esi, 6
      mov edx, 173
      call fmt::v7::vprint(fmt::v7::basic_string_view<char>, fmt::v7::format_args)
      add rsp, 40
      ret
    

    你看,没有算法开销,只是取消引用。


    ¹ 实际上,属性存储在一种类似于 Lisp 的列表类型中,其最终行为类似于元组。 Boost Graph 比元组早了相当长的时间。我想如果你想要一个可以将它与 Boost Fusion 的 map 类型(也有一个类型键)进行比较。

    【讨论】:

    • 出于好奇,检查了代码生成。 godbolt.org/z/fEn75h7dK 布丁的证据就在吃,正如他们所说的
    • 行内:test(vertex(0, g), e, g);,因为我们将顶点存储在std::list 中,所以对vertex(0, g) 的调用不是线性时间吗? 0 在算法上是否类似于在 list 上应用 [0] 并因此是线性的而不是恒定的时间?
    • 您的问题是关于属性映射查找的,对吧?如何获得描述符是您关心的问题。在您的问题中,您使用了循环(线性迭代)。我选择了一个任意的有效描述符来调用函数。关键是从描述符中检索属性是常数时间,这就是问题所在。 (另外,不能在list 上应用[0],所以我不确定这是什么意思)
    猜你喜欢
    • 2010-10-14
    • 1970-01-01
    • 2015-05-02
    • 1970-01-01
    • 1970-01-01
    • 2012-05-01
    • 1970-01-01
    • 2011-12-08
    • 2016-05-03
    相关资源
    最近更新 更多