// Copyright 2004 The Trustees of Indiana University. // Use, modification and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // Authors: Nick Edmonds // Andrew Lumsdaine // #define PBGL_ACCOUNTING #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Edge distribution tags select a generator struct rmat_edge_distribution_tag { }; struct rmat_unique_edge_distribution_tag { }; using namespace boost; using boost::graph::distributed::mpi_process_group; /**************************************************************************** * Timing ****************************************************************************/ #ifndef PBGL_ACCOUNTING typedef double time_type; inline time_type get_time() { timeval tp; gettimeofday(&tp, 0); return tp.tv_sec + tp.tv_usec / 1000000.0; } std::string print_time(time_type t) { std::ostringstream out; out << std::setiosflags(std::ios::fixed) << std::setprecision(2) << t; return out.str(); } #endif // PBGL_ACCOUNTING /**************************************************************************** * Edge weight generator iterator * ****************************************************************************/ template class generator_iterator { public: typedef std::input_iterator_tag iterator_category; typedef typename F::result_type value_type; typedef const value_type& reference; typedef const value_type* pointer; typedef void difference_type; explicit generator_iterator(RandomGenerator& gen, const F& f = F()) : f(f), gen(&gen) { value = this->f(gen); } reference operator*() const { return value; } pointer operator->() const { return &value; } generator_iterator& operator++() { value = f(*gen); return *this; } generator_iterator operator++(int) { generator_iterator temp(*this); ++(*this); return temp; } bool operator==(const generator_iterator& other) const { return f == other.f; } bool operator!=(const generator_iterator& other) const { return !(*this == other); } private: F f; RandomGenerator* gen; value_type value; }; template inline generator_iterator make_generator_iterator( RandomGenerator& gen, const F& f) { return generator_iterator(gen, f); } /**************************************************************************** * Edge Property * ****************************************************************************/ typedef int weight_type; struct WeightedEdge { WeightedEdge(weight_type weight = 0) : weight(weight) { } weight_type weight; template void serialize(Archiver& ar, const unsigned int /*version*/) { ar & weight; } }; /**************************************************************************** * Algorithm Tests * ****************************************************************************/ template void test_directed_sequential_algorithms(const Graph& g) { } template void test_undirected_sequential_algorithms(const Graph& g) { std::vector componentS(num_vertices(g)); typedef iterator_property_map< std::vector::iterator, typename property_map::type> ComponentMap; ComponentMap component(componentS.begin(), get(vertex_index, g)); time_type start = get_time(); unsigned int num_components = connected_components(g, component); time_type end = get_time(); std::cerr << " Sequential connected Components time = " << print_time(end - start) << " seconds.\n" << " " << num_components << " components identified\n"; } template void test_directed_csr_only_algorithms(const Graph& g, EdgeWeightMap weight, typename graph_traits::vertices_size_type num_sources, typename property_traits::value_type C) { typedef typename graph_traits::vertex_descriptor vertex_descriptor; typedef typename graph_traits::vertex_iterator vertex_iterator; typedef typename graph_traits::vertices_size_type vertices_size_type; typedef typename graph_traits::edges_size_type edges_size_type; typedef typename boost::graph::parallel::process_group_type::type process_group_type; process_group_type pg = process_group(g); typename process_group_type::process_id_type id = process_id(pg); typename process_group_type::process_size_type p = num_processes(pg); vertices_size_type n = num_vertices(g); n = boost::parallel::all_reduce(pg, n, std::plus()); edges_size_type m = num_edges(g); m = boost::parallel::all_reduce(pg, m, std::plus()); // // Betweenness Centrality (Approximate) // queue delta_stepping_vertices; { // Distributed Centrality Map typedef typename property_map::const_type IndexMap; typedef iterator_property_map::iterator, IndexMap> CentralityMap; std::vector centralityS(num_vertices(g), 0); CentralityMap centrality(centralityS.begin(), get(vertex_index, g)); // Calculate number of vertices of degree 0 vertices_size_type n0 = 0; BGL_FORALL_VERTICES_T(v, g, Graph) { if (out_degree(v, g) == 0) n0++; } n0 = boost::parallel::all_reduce(pg, n0, std::plus()); queue sources; { assert(num_sources >= p); // Don't feel like adding a special case for num_sources < p minstd_rand gen; uniform_int rand_vertex(0, num_vertices(g) - 1); std::vector all_sources, local_sources; vertices_size_type local_vertices = vertices_size_type(floor((double)num_sources / p)); local_vertices += (id < (num_sources - (p * local_vertices)) ? 1 : 0); while (local_vertices > 0) { vertex_iterator iter = vertices(g).first; std::advance(iter, rand_vertex(gen)); if (out_degree(*iter, g) != 0 && std::find(local_sources.begin(), local_sources.end(), *iter) == local_sources.end()) { local_sources.push_back(*iter); --local_vertices; } } all_gather(pg, local_sources.begin(), local_sources.end(), all_sources); std::sort(all_sources.begin(), all_sources.end()); for (typename std::vector::iterator iter = all_sources.begin(); iter != all_sources.end(); ++iter) { sources.push(*iter); delta_stepping_vertices.push(*iter); } } // NOTE: The delta below assumes uniform edge weight distribution time_type start = get_time(); brandes_betweenness_centrality(g, buffer(sources).weight_map(weight). centrality_map(centrality).lookahead((m / n) * (C / 2))); time_type end = get_time(); edges_size_type exactTEPs = edges_size_type(floor(7 * n* (n - n0) / (end - start))); if (id == 0) std::cerr << " Betweenness Centrality Approximate (" << num_sources << " sources) = " << print_time(end - start) << " (" << exactTEPs << " TEPs)\n"; } // // Delta stepping performance (to compare to SSSP inside BC // if (false) { typedef typename property_map::const_type IndexMap; typedef iterator_property_map::iterator, IndexMap> DistanceMap; std::vector distanceS(num_vertices(g), 0); DistanceMap distance(distanceS.begin(), get(vertex_index, g)); while(!delta_stepping_vertices.empty()) { time_type start = get_time(); delta_stepping_shortest_paths(g, delta_stepping_vertices.top(), dummy_property_map(), distance, weight); time_type end = get_time(); delta_stepping_vertices.pop(); distance.reset(); if (id == 0) std::cerr << " Delta-stepping shortest paths = " << print_time(end - start) << std::endl; } } } template void test_directed_algorithms(const Graph& g) { } template void test_undirected_algorithms(const Graph& g) { typedef typename boost::graph::parallel::process_group_type::type process_group_type; process_group_type pg = process_group(g); typename process_group_type::process_id_type id = process_id(pg); typename process_group_type::process_size_type p = num_processes(pg); // Connected Components std::vector local_components_vec(num_vertices(g)); typedef iterator_property_map< std::vector::iterator, typename property_map::type> ComponentMap; ComponentMap component(local_components_vec.begin(), get(vertex_index, g)); int num_components; time_type start = get_time(); num_components = connected_components(g, component); time_type end = get_time(); if (id == 0) std::cerr << " Connected Components time = " << print_time(end - start) << " seconds.\n" << " " << num_components << " components identified\n"; start = get_time(); num_components = boost::graph::distributed::connected_components_ps(g, component); end = get_time(); if (id == 0) std::cerr << " Connected Components (parallel search) time = " << print_time(end - start) << " seconds.\n" << " " << num_components << " components identified\n"; } /**************************************************************************** * Graph Type Tests * ****************************************************************************/ // TODO: Re-seed PRNG before sequential tests to get the same graph as the // distributed case? // // Compressed Sparse Row // // R-MAT template void test_csr(const ProcessGroup& pg, RandomGenerator& gen, Distribution& distrib, bool sequential_tests, size_t N, size_t M, size_t C, double a, double b, double c, double d, size_t num_sources, rmat_edge_distribution_tag) { if (process_id(pg) == 0) std::cerr << " R-MAT\n"; typedef compressed_sparse_row_graph > Graph; Graph g(sorted_rmat_iterator(gen, N, M, a, b, c, d), sorted_rmat_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N, pg, distrib); test_directed_algorithms(g); test_directed_csr_only_algorithms(g, get(&WeightedEdge::weight, g), num_sources, C); if (sequential_tests && process_id(pg) == 0) { typedef compressed_sparse_row_graph seqGraph; seqGraph sg(edges_are_sorted, sorted_rmat_iterator(gen, N, M, a, b, c, d), sorted_rmat_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N); test_directed_sequential_algorithms(sg); } } // R-MAT with unique edges template void test_csr(const ProcessGroup& pg, RandomGenerator& gen, Distribution& distrib, bool sequential_tests, size_t N, size_t M, size_t C, double a, double b, double c, double d, size_t num_sources, rmat_unique_edge_distribution_tag) { if (process_id(pg) == 0) std::cerr << " R-MAT - unique\n"; typedef compressed_sparse_row_graph > Graph; // Last boolean parameter makes R-MAT bidirectional Graph g(sorted_unique_rmat_iterator(gen, N, M, a, b, c, d), sorted_unique_rmat_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N, pg, distrib); test_directed_algorithms(g); test_directed_csr_only_algorithms(g, get(&WeightedEdge::weight, g), num_sources, C); if (sequential_tests && process_id(pg) == 0) { typedef compressed_sparse_row_graph seqGraph; seqGraph sg(edges_are_sorted, sorted_unique_rmat_iterator(gen, N, M, a, b, c, d), sorted_unique_rmat_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N); test_directed_sequential_algorithms(sg); } } // Erdos-Renyi template void test_csr(const ProcessGroup& pg, RandomGenerator& gen, Distribution& distrib, bool sequential_tests, size_t N, size_t M, size_t C, size_t num_sources) { if (process_id(pg) == 0) std::cerr << " Erdos-Renyi\n"; double _p = static_cast(M) / (N*N); typedef compressed_sparse_row_graph > Graph; Graph g(sorted_erdos_renyi_iterator(gen, N, _p/2), sorted_erdos_renyi_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N, pg, distrib); test_directed_algorithms(g); test_directed_csr_only_algorithms(g, get(&WeightedEdge::weight, g), num_sources, C); if (sequential_tests && process_id(pg) == 0) { typedef compressed_sparse_row_graph seqGraph; seqGraph sg(edges_are_sorted, sorted_erdos_renyi_iterator(gen, N, _p/2), sorted_erdos_renyi_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N); test_directed_sequential_algorithms(sg); } } // Small World template void test_csr(const ProcessGroup& pg, RandomGenerator& gen, Distribution& distrib, bool sequential_tests, size_t N, size_t M, size_t C, double p, size_t num_sources) { if (process_id(pg) == 0) std::cerr << " Small-World\n"; int k = M / N; typedef compressed_sparse_row_graph > Graph; Graph g(small_world_iterator(gen, N, k, p), small_world_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N, pg, distrib); test_directed_algorithms(g); test_directed_csr_only_algorithms(g, get(&WeightedEdge::weight, g), num_sources, C); if (sequential_tests && process_id(pg) == 0) { typedef compressed_sparse_row_graph seqGraph; seqGraph sg(edges_are_sorted, small_world_iterator(gen, N, k, p), small_world_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N); test_directed_sequential_algorithms(sg); } } // // Adjacency List // // R-MAT template void test_adjacency_list(const ProcessGroup& pg, RandomGenerator& gen, Distribution& distrib, bool sequential_tests, size_t N, size_t M, size_t C, double a, double b, double c, double d, rmat_edge_distribution_tag) { if (process_id(pg) == 0) std::cerr << "R-MAT\n"; { typedef adjacency_list, directedS, no_property, WeightedEdge> Graph; Graph g(rmat_iterator(gen, N, M, a, b, c, d), rmat_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N, pg, distrib); test_directed_algorithms(g); } { typedef adjacency_list, undirectedS, no_property, WeightedEdge> Graph; Graph g(rmat_iterator(gen, N, M, a, b, c, d), rmat_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N, pg, distrib); test_undirected_algorithms(g); } if (sequential_tests && process_id(pg) == 0) { { typedef adjacency_list > seqGraph; seqGraph sg(rmat_iterator(gen, N, M, a, b, c, d), rmat_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N); test_directed_sequential_algorithms(sg); } { typedef adjacency_list > seqGraph; seqGraph sg(rmat_iterator(gen, N, M, a, b, c, d), rmat_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N); test_undirected_sequential_algorithms(sg); } } } // R-MAT with unique edges template void test_adjacency_list(const ProcessGroup& pg, RandomGenerator& gen, Distribution& distrib, bool sequential_tests, size_t N, size_t M, size_t C, double a, double b, double c, double d, rmat_unique_edge_distribution_tag) { if (process_id(pg) == 0) std::cerr << " R-MAT - unique\n"; { typedef adjacency_list, directedS, no_property, WeightedEdge> Graph; Graph g(sorted_unique_rmat_iterator(gen, N, M, a, b, c, d), sorted_unique_rmat_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N, pg, distrib); test_directed_algorithms(g); } { typedef adjacency_list, undirectedS, no_property, WeightedEdge> Graph; Graph g(sorted_unique_rmat_iterator(gen, N, M, a, b, c, d), sorted_unique_rmat_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N, pg, distrib); test_undirected_algorithms(g); } if (sequential_tests && process_id(pg) == 0) { { typedef adjacency_list > seqGraph; seqGraph sg(sorted_unique_rmat_iterator(gen, N, M, a, b, c, d), sorted_unique_rmat_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N); test_directed_sequential_algorithms(sg); } { typedef adjacency_list > seqGraph; seqGraph sg(sorted_unique_rmat_iterator(gen, N, M, a, b, c, d), sorted_unique_rmat_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N); test_undirected_sequential_algorithms(sg); } } } // Erdos-Renyi template void test_adjacency_list(const ProcessGroup& pg, RandomGenerator& gen, Distribution& distrib, bool sequential_tests, size_t N, size_t M, size_t C) { if (process_id(pg) == 0) std::cerr << " Erdos-Renyi\n"; double _p = static_cast(M) / N*N; { typedef adjacency_list, directedS, no_property, WeightedEdge> Graph; Graph g(erdos_renyi_iterator(gen, N, _p/2), erdos_renyi_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N, pg, distrib); test_directed_algorithms(g); } { typedef adjacency_list, undirectedS, no_property, WeightedEdge> Graph; Graph g(erdos_renyi_iterator(gen, N, _p/2), erdos_renyi_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N, pg, distrib); test_undirected_algorithms(g); } if (sequential_tests && process_id(pg) == 0) { { typedef adjacency_list > seqGraph; seqGraph sg(erdos_renyi_iterator(gen, N, _p/2), erdos_renyi_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N); test_directed_sequential_algorithms(sg); } { typedef adjacency_list > seqGraph; seqGraph sg(erdos_renyi_iterator(gen, N, _p/2), erdos_renyi_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N); test_undirected_sequential_algorithms(sg); } } } // Small World template void test_adjacency_list(const ProcessGroup& pg, RandomGenerator& gen, Distribution& distrib, bool sequential_tests, size_t N, size_t M, size_t C, double p) { if (process_id(pg) == 0) std::cerr << " Small-World\n"; int k = M / N; { typedef adjacency_list, directedS, no_property, WeightedEdge> Graph; Graph g(small_world_iterator(gen, N, k, p), small_world_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N, pg, distrib); test_directed_algorithms(g); } { typedef adjacency_list, undirectedS, no_property, WeightedEdge> Graph; Graph g(small_world_iterator(gen, N, k, p), small_world_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N, pg, distrib); test_undirected_algorithms(g); } if (sequential_tests && process_id(pg) == 0) { { typedef adjacency_list > seqGraph; seqGraph sg(small_world_iterator(gen, N, k, p), small_world_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N); test_directed_sequential_algorithms(sg); } { typedef adjacency_list > seqGraph; seqGraph sg(small_world_iterator(gen, N, k, p), small_world_iterator(), make_generator_iterator(gen, uniform_int(1, C)), N); test_undirected_sequential_algorithms(sg); } } } void usage() { std::cerr << "Algorithm Performance Test\n" << "Usage : algorithm_performance [options]\n\n" << "Options are:\n" << "\t--vertices v\t\t\tNumber of vertices in the graph\n" << "\t--edges v\t\t\tNumber of edges in the graph\n" << "\t--seed s\t\t\tSeed for synchronized random number generator\n" << "\t--max-weight miw\t\tMaximum integer edge weight\n" << "\t--rewire-probability\t\tRewire-probabitility (p) for small-world graphs\n" << "\t--dot\t\t\t\tEmit a dot file containing the graph\n" << "\t--verify\t\t\tVerify result\n" << "\t--degree-dist\t\t\tOutput degree distribution of graph\n" << "\t--sequential-graph\t\tRun sequential graph tests\n" << "\t--erdos-renyi\t\t\tRun tests on Erdos-Renyi graphs\n" << "\t--small-world\t\t\tRun tests on Small World graphs\n" << "\t--rmat\t\t\t\tRun tests on R-MAT graphs\n" << "\t--rmat-unique\t\t\tRun tests on R-MAT graphs with no duplicate edges\n" << "\t--csr \t\t\tRun tests using CSR graphs\n" << "\t--adjacency-list \t\tRun tests using Adjacency List graphs\n"; } int test_main(int argc, char* argv[]) { mpi::environment env(argc, argv); rand48 gen; // Default args size_t n = 100, m = 8*n, c = 100, num_sources = 32, num_headers = 16 * 1024, batch_buffer_size = 1024 * 1024; uint64_t seed = 1; bool emit_dot_file = false, verify = false, sequential_graph = false, show_degree_dist = false, erdos_renyi = false, small_world = false, rmat = false, rmat_unique = false, csr = false, adj_list = false; double p = 0.1, rmat_a = 0.5, rmat_b = 0.25, rmat_c = 0.1, rmat_d = 0.15; // Parse args for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; if (arg == "--vertices") n = boost::lexical_cast( argv[i+1] ); if (arg == "--seed") seed = boost::lexical_cast( argv[i+1] ); if (arg == "--edges") m = boost::lexical_cast( argv[i+1] ); if (arg == "--max-weight") c = boost::lexical_cast( argv[i+1] ); if (arg == "--batch-buffer-size") { batch_buffer_size = boost::lexical_cast( argv[i+1] ); num_headers = batch_buffer_size / 16; num_headers = num_headers > 0 ? num_headers : 1; } if (arg == "--rewire-probability") p = boost::lexical_cast( argv[i+1] ); if (arg == "--num-sources") num_sources = boost::lexical_cast( argv[i + 1] ); if (arg == "--erdos-renyi") erdos_renyi = true; if (arg == "--small-world") small_world = true; if (arg == "--rmat") rmat = true; if (arg == "--rmat-unique") rmat_unique = true; if (arg == "--dot") emit_dot_file = true; if (arg == "--verify") verify = true; if (arg == "--degree-dist") show_degree_dist = true; if (arg == "--sequential-graph") sequential_graph = true; if (arg == "--csr") csr = std::string(argv[i+1]) == "true"; if (arg == "--adjacency-list") adj_list = std::string(argv[i+1]) == "true"; } mpi_process_group pg(num_headers, batch_buffer_size); if (argc == 1) { if (process_id(pg) == 0) usage(); exit(-1); } gen.seed(seed); parallel::variant_distribution distrib = parallel::block(pg, n); if (csr) { if (process_id(pg) == 0) std::cerr << "CSR Graph Tests\n"; if (erdos_renyi) test_csr(pg, gen, distrib, sequential_graph, n, m, c, num_sources); if (small_world) test_csr(pg, gen, distrib, sequential_graph, n, m, c, p, num_sources); if (rmat) test_csr(pg, gen, distrib, sequential_graph, n, m, c, rmat_a, rmat_b, rmat_c, rmat_d, num_sources, rmat_edge_distribution_tag()); if (rmat_unique) test_csr(pg, gen, distrib, sequential_graph, n, m, c, rmat_a, rmat_b, rmat_c, rmat_d, num_sources, rmat_unique_edge_distribution_tag()); } gen.seed(seed); // DEBUG if (adj_list) { if (process_id(pg) == 0) std::cerr << "Adjacency List Graph Tests\n"; if (erdos_renyi) test_adjacency_list(pg, gen, distrib, sequential_graph, n, m, c); if (small_world) test_adjacency_list(pg, gen, distrib, sequential_graph, n, m, c, p); if (rmat) test_adjacency_list(pg, gen, distrib, sequential_graph, n, m, c, rmat_a, rmat_b, rmat_c, rmat_d, rmat_edge_distribution_tag()); if (rmat_unique) test_adjacency_list(pg, gen, distrib, sequential_graph, n, m, c, rmat_a, rmat_b, rmat_c, rmat_d, rmat_unique_edge_distribution_tag()); } return 0; }