C++ Boost:大图&;多线程

C++ Boost:大图&;多线程,c++,multithreading,boost,graph,boost-graph,C++,Multithreading,Boost,Graph,Boost Graph,我需要从一个大数据集中创建一个非常大的有向图。我确信这些事情: 每个节点最多有K条传出边 我有一个N>>K个节点的列表(unordered\u map) 该图是通过比较所有节点来构建的(是的,很遗憾是O(N^2)) 考虑到这一点,我将使用std::thread并行创建图形,我想知道这是否可以通过Boost图形库实现 如果我使用邻接矩阵,应该可以预先分配矩阵(K*N个元素),因此插入所有相邻节点是线程安全的 我读过BGL可能是线程不安全的,但我发现的帖子已经有三年了 你知道有没有可能像我想的那

我需要从一个大数据集中创建一个非常大的有向图。我确信这些事情:

  • 每个节点最多有K条传出边
  • 我有一个N>>K个节点的列表(
    unordered\u map
  • 该图是通过比较所有节点来构建的(是的,很遗憾是O(N^2))
考虑到这一点,我将使用
std::thread
并行创建图形,我想知道这是否可以通过Boost图形库实现

如果我使用邻接矩阵,应该可以预先分配矩阵(K*N个元素),因此插入所有相邻节点是线程安全的

我读过BGL可能是线程不安全的,但我发现的帖子已经有三年了

你知道有没有可能像我想的那样?你建议换个方式吗


干杯

我认为你应该把你的目标分解成两个独立的子目标

  • 通过对节点对进行N*(N-1)测试来创建节点之间的链接。您似乎知道如何将其分解为独立的线程。将结果存储在您知道是线程安全的数据结构中,而不用担心boost:graph的奥秘

  • 从节点和(刚刚创建的)链接创建boost::graph

  • 关于存储每个线程中创建的链接的注意事项:要找到合适的线程安全数据结构并不容易。如果您使用STL动态分配结构,那么您必须担心如何创建线程安全的分配器,这是一个挑战。如果您预先分配,那么会有很多meessy代码来处理分配。因此,我建议将每个线程创建的链接存储在单独的数据结构中,这样它们就不必是线程安全的。创建完所有链接后,可以逐个循环每个线程创建的链接


    一个稍微高效的设计是可以想象的,但需要大量关于线程安全的神秘知识。我提出的设计可以在没有晦涩知识或复杂代码的情况下实现,因此实现起来更快、更稳健,更易于维护。

    我认为您应该将目标分解为两个单独的子目标

  • 通过对节点对进行N*(N-1)测试来创建节点之间的链接。您似乎知道如何将其分解为独立的线程。将结果存储在您知道是线程安全的数据结构中,而不用担心boost:graph的奥秘

  • 从节点和(刚刚创建的)链接创建boost::graph

  • 关于存储每个线程中创建的链接的注意事项:要找到合适的线程安全数据结构并不容易。如果您使用STL动态分配结构,那么您必须担心如何创建线程安全的分配器,这是一个挑战。如果您预先分配,那么会有很多meessy代码来处理分配。因此,我建议将每个线程创建的链接存储在单独的数据结构中,这样它们就不必是线程安全的。创建完所有链接后,可以逐个循环每个线程创建的链接


    一个稍微高效的设计是可以想象的,但需要大量关于线程安全的神秘知识。我提出的设计可以在没有晦涩知识或复杂代码的情况下实现,因此实现起来更快、更稳健,也更易于维护。

    几乎BGL中的任何图形算法都需要一个映射:顶点->整数,它为每个顶点指定一个[0,num_顶点(g)范围内的唯一整数)。此映射称为“顶点索引”,通常可作为属性映射访问

    话虽如此,我可以假设您的顶点已经是整数或与某些整数关联(例如,无序的_映射在“mapped_type”中有一些额外字段)。如果您的输入顶点存储在连续的紧数组中(例如std::vector),则索引是自然的(为了性能和内存)

    如果顶点与整数[关联],则内存紧张图的最佳选择是“”。该图是不可变的,因此需要在生成图之前填充边容器

    正如RavenPoint所解释的,您的最佳选择是为每个线程配备其自己的本地结果容器,并仅在将本地结果合并到最终结果时锁定中心容器。这种策略通过TBB模板实现无锁。因此,图形构建的完整代码大致如下所示:

    #include "tbb/blocked_range2d.h"
    #include "tbb/parallel_reduce.h"
    #include "boost/graph/compressed_sparse_row_graph.hpp"
    
    typedef something vertex; //e.g.something is integer giving index of a real data
    
    class EdgeBuilder
    {
    public:
        typedef std::pair<int,int> edge;
        typedef std::vector<edge> Edges;
        typedef ActualStorage Input;
    
        EdgeBuilder(const Input & input):_input(input){} //OPTIONAL: reserve some space in _edges
        EdgeBuilder( EdgeBuilder& parent, tbb::split ): _input(parent.input){} // reserve something
    
        void operator()( const const tbb::blocked_range2d<size_t>& r ) 
        { 
            for( size_t i=r.rows().begin(); i!=r.rows().end(); ++i ){
                for( size_t j=r.cols().begin(); j!=r.cols().end(); ++j ) {
                    //I assume you provide some function to compute existence
                    if (my_func_edge_exist(_input,i, j))
                        m_edges.push_back(edge(i,j));
                }
            }        
        } 
    
        //merges local results from two TBB threads
        void join( EdgeBuilder& rhs ) 
        {
            m_edges.insert( m_edges.end(), rhs.m_edges.begin(), rhs.m_edges.end() ); 
        }
    
        Edges _edges; //for a given interval of vertices
        const Input & _input;
    };
    
    //full flow:  
    boost::compressed_sparse_row_graph<>* build_graph( const Storage & vertices)
    {
        EdgeBuilder builder(vertices);
        tbb::blocked_range2d<size_t,size_t> range(0,vertices.size(), 100, //row grain size 
                                                  0,vertices.size(), 100); //col grain size
        tbb::parallel_reduce(range, builder);
    
        boost::compressed_sparse_row_graph<> 
          theGraph = new boost::compressed_sparse_row_graph<> 
                            (boost::edges_are_unsorted_multi_pass_t, 
                             builder._edges.begin(), builder._edges.end(), 
                             vertices.size() );
        return theGraph;
    }
    
    #包括“tbb/blocked_range2d.h”
    #包括“tbb/parallel_reduce.h”
    #包括“boost/graph/compressed\u sparse\u row\u graph.hpp”
    typedef something vertex;//例如,something是一个整数,给出实际数据的索引
    类EdgeBuilder
    {
    公众:
    typedef std::对边;
    typedef std::向量边;
    类型定义实际存储输入;
    EdgeBuilder(常量输入和输入):\u输入(输入){}//可选:在\u边中保留一些空间
    EdgeBuilder(EdgeBuilder&parent,tbb::split):\u输入(parent.input){}//保留某些内容
    void运算符()
    { 
    对于(大小i=r.rows().begin();i!=r.rows().end();++i){
    对于(大小j=r.cols().begin();j!=r.cols().end();++j){
    //我假设你提供了一些函数来计算存在性
    如果(我的函数边存在(_输入,i,j))
    m_边。向后推(边(i,j));
    }
    }        
    } 
    //合并来自两个TBB线程的本地结果
    无效连接(EdgeBuilder和rhs)
    {
    m_edges.insert(m_edges.end()、rhs.m_edges.begin()、rhs.m_edges.end());
    }
    边_边;//对于给定的顶点间隔
    常量输入&_输入;
    };
    //全流量:
    boost::压缩的稀疏行图*构建图(常量存储和顶点)
    {
    EdgeBuilder生成器(顶点);
    tbb::阻塞范围2D范围(