diff --git a/contractor/contractor.hpp b/contractor/contractor.hpp index 6fc24052b..07a21dc50 100644 --- a/contractor/contractor.hpp +++ b/contractor/contractor.hpp @@ -158,6 +158,15 @@ class Contractor public: template Contractor(int nodes, ContainerT &input_edge_list) + : Contractor(nodes, input_edge_list, {}, {}) + { + } + + template + Contractor(int nodes, + ContainerT &input_edge_list, + std::vector &&node_levels_) + : node_levels(std::move(node_levels_)) { std::vector edges; edges.reserve(input_edge_list.size() * 2); @@ -305,14 +314,14 @@ class Contractor ThreadDataContainer thread_data_list(number_of_nodes); NodeID number_of_contracted_nodes = 0; - std::vector remaining_nodes(number_of_nodes); - std::vector node_priorities(number_of_nodes); - std::vector node_data(number_of_nodes); + std::vector node_data; + std::vector node_priorities; is_core_node.resize(number_of_nodes, false); + std::vector remaining_nodes(number_of_nodes); // initialize priorities in parallel tbb::parallel_for(tbb::blocked_range(0, number_of_nodes, InitGrainSize), - [&remaining_nodes](const tbb::blocked_range &range) + [this, &remaining_nodes](const tbb::blocked_range &range) { for (int x = range.begin(), end = range.end(); x != end; ++x) { @@ -320,21 +329,38 @@ class Contractor } }); - std::cout << "initializing elimination PQ ..." << std::flush; - tbb::parallel_for(tbb::blocked_range(0, number_of_nodes, PQGrainSize), - [this, &node_priorities, &node_data, &thread_data_list]( - const tbb::blocked_range &range) - { - ContractorThreadData *data = thread_data_list.getThreadData(); - for (int x = range.begin(), end = range.end(); x != end; ++x) - { - node_priorities[x] = - this->EvaluateNodePriority(data, &node_data[x], x); - } - }); - std::cout << "ok" << std::endl - << "preprocessing " << number_of_nodes << " nodes ..." << std::flush; + bool use_cached_node_priorities = !node_levels.empty(); + if (use_cached_node_priorities) + { + std::cout << "using cached node priorities ..." << std::flush; + node_priorities.swap(node_levels); + std::cout << "ok" << std::endl; + } + else + { + node_data.resize(number_of_nodes); + node_priorities.resize(number_of_nodes); + node_levels.resize(number_of_nodes); + std::cout << "initializing elimination PQ ..." << std::flush; + tbb::parallel_for(tbb::blocked_range(0, number_of_nodes, PQGrainSize), + [this, &node_priorities, &node_data, &thread_data_list]( + const tbb::blocked_range &range) + { + ContractorThreadData *data = thread_data_list.getThreadData(); + for (int x = range.begin(), end = range.end(); x != end; ++x) + { + node_priorities[x] = + this->EvaluateNodePriority(data, &node_data[x], x); + } + }); + std::cout << "ok" << std::endl; + } + BOOST_ASSERT(node_priorities.size() == number_of_nodes); + + std::cout << "preprocessing " << number_of_nodes << " nodes ..." << std::flush; + + unsigned current_level = 0; bool flushed_contractor = false; while (number_of_nodes > 2 && number_of_contracted_nodes < static_cast(number_of_nodes * core_factor)) @@ -359,29 +385,32 @@ class Contractor // remaining graph std::vector new_node_id_from_orig_id_map(number_of_nodes, UINT_MAX); - // build forward and backward renumbering map and remap ids in remaining_nodes and - // Priorities. for (const auto new_node_id : osrm::irange(0, remaining_nodes.size())) { + auto& node = remaining_nodes[new_node_id]; + BOOST_ASSERT(node_priorities.size() > node.id); + new_node_priority[new_node_id] = node_priorities[node.id]; + } + + // build forward and backward renumbering map and remap ids in remaining_nodes + for (const auto new_node_id : osrm::irange(0, remaining_nodes.size())) + { + auto& node = remaining_nodes[new_node_id]; // create renumbering maps in both directions - orig_node_id_from_new_node_id_map[new_node_id] = - remaining_nodes[new_node_id].id; - new_node_id_from_orig_id_map[remaining_nodes[new_node_id].id] = new_node_id; - new_node_priority[new_node_id] = - node_priorities[remaining_nodes[new_node_id].id]; - remaining_nodes[new_node_id].id = new_node_id; + orig_node_id_from_new_node_id_map[new_node_id] = node.id; + new_node_id_from_orig_id_map[node.id] = new_node_id; + node.id = new_node_id; } // walk over all nodes - for (const auto i : - osrm::irange(0, contractor_graph->GetNumberOfNodes())) + for (const auto source : + osrm::irange(0, contractor_graph->GetNumberOfNodes())) { - const NodeID source = i; for (auto current_edge : contractor_graph->GetAdjacentEdgeRange(source)) { ContractorGraph::EdgeData &data = contractor_graph->GetEdgeData(current_edge); const NodeID target = contractor_graph->GetTarget(current_edge); - if (SPECIAL_NODEID == new_node_id_from_orig_id_map[i]) + if (SPECIAL_NODEID == new_node_id_from_orig_id_map[source]) { external_edge_list.push_back({source, target, data}); } @@ -428,14 +457,13 @@ class Contractor thread_data_list.number_of_nodes = contractor_graph->GetNumberOfNodes(); } - const int last = (int)remaining_nodes.size(); - tbb::parallel_for(tbb::blocked_range(0, last, IndependentGrainSize), + tbb::parallel_for(tbb::blocked_range(0, remaining_nodes.size(), IndependentGrainSize), [this, &node_priorities, &remaining_nodes, &thread_data_list]( - const tbb::blocked_range &range) + const tbb::blocked_range &range) { ContractorThreadData *data = thread_data_list.getThreadData(); // determine independent node set - for (int i = range.begin(), end = range.end(); i != end; ++i) + for (auto i = range.begin(), end = range.end(); i != end; ++i) { const NodeID node = remaining_nodes[i].id; remaining_nodes[i].is_independent = @@ -443,17 +471,45 @@ class Contractor } }); - const auto first = stable_partition(remaining_nodes.begin(), remaining_nodes.end(), + // sort all remaining nodes to the beginning of the sequence + const auto begin_independent_nodes = stable_partition(remaining_nodes.begin(), remaining_nodes.end(), [](RemainingNodeData node_data) { return !node_data.is_independent; }); - const int first_independent_node = static_cast(first - remaining_nodes.begin()); + auto begin_independent_nodes_idx = std::distance(remaining_nodes.begin(), begin_independent_nodes); + auto end_independent_nodes_idx = remaining_nodes.size(); + + if (!use_cached_node_priorities) + { + // write out contraction level + tbb::parallel_for( + tbb::blocked_range(begin_independent_nodes_idx, end_independent_nodes_idx, ContractGrainSize), + [this, remaining_nodes, flushed_contractor, current_level](const tbb::blocked_range &range) + { + if (flushed_contractor) + { + for (int position = range.begin(), end = range.end(); position != end; ++position) + { + const NodeID x = remaining_nodes[position].id; + node_levels[orig_node_id_from_new_node_id_map[x]] = current_level; + } + } + else + { + for (int position = range.begin(), end = range.end(); position != end; ++position) + { + const NodeID x = remaining_nodes[position].id; + node_levels[x] = current_level; + } + } + }); + } // contract independent nodes tbb::parallel_for( - tbb::blocked_range(first_independent_node, last, ContractGrainSize), - [this, &remaining_nodes, &thread_data_list](const tbb::blocked_range &range) + tbb::blocked_range(begin_independent_nodes_idx, end_independent_nodes_idx, ContractGrainSize), + [this, &remaining_nodes, &thread_data_list](const tbb::blocked_range &range) { ContractorThreadData *data = thread_data_list.getThreadData(); for (int position = range.begin(), end = range.end(); position != end; ++position) @@ -462,17 +518,9 @@ class Contractor this->ContractNode(data, x); } }); - // make sure we really sort each block + tbb::parallel_for( - thread_data_list.data.range(), - [&](const ThreadDataContainer::EnumerableThreadData::range_type &range) - { - for (auto &data : range) - tbb::parallel_sort(data->inserted_edges.begin(), - data->inserted_edges.end()); - }); - tbb::parallel_for( - tbb::blocked_range(first_independent_node, last, DeleteGrainSize), + tbb::blocked_range(begin_independent_nodes_idx, end_independent_nodes_idx, DeleteGrainSize), [this, &remaining_nodes, &thread_data_list](const tbb::blocked_range &range) { ContractorThreadData *data = thread_data_list.getThreadData(); @@ -483,6 +531,16 @@ class Contractor } }); + // make sure we really sort each block + tbb::parallel_for( + thread_data_list.data.range(), + [&](const ThreadDataContainer::EnumerableThreadData::range_type &range) + { + for (auto &data : range) + tbb::parallel_sort(data->inserted_edges.begin(), + data->inserted_edges.end()); + }); + // insert new edges for (auto &data : thread_data_list.data) { @@ -508,23 +566,25 @@ class Contractor data->inserted_edges.clear(); } - tbb::parallel_for( - tbb::blocked_range(first_independent_node, last, NeighboursGrainSize), - [this, &remaining_nodes, &node_priorities, &node_data, &thread_data_list]( - const tbb::blocked_range &range) - { - ContractorThreadData *data = thread_data_list.getThreadData(); - for (int position = range.begin(), end = range.end(); position != end; ++position) + if (!use_cached_node_priorities) + { + tbb::parallel_for( + tbb::blocked_range(begin_independent_nodes_idx, end_independent_nodes_idx, NeighboursGrainSize), + [this, &node_priorities, &remaining_nodes, &node_data, &thread_data_list]( + const tbb::blocked_range &range) { - NodeID x = remaining_nodes[position].id; - this->UpdateNodeNeighbours(node_priorities, node_data, data, x); - } - }); + ContractorThreadData *data = thread_data_list.getThreadData(); + for (int position = range.begin(), end = range.end(); position != end; ++position) + { + NodeID x = remaining_nodes[position].id; + this->UpdateNodeNeighbours(node_priorities, node_data, data, x); + } + }); + } // remove contracted nodes from the pool - number_of_contracted_nodes += last - first_independent_node; - remaining_nodes.resize(first_independent_node); - remaining_nodes.shrink_to_fit(); + number_of_contracted_nodes += end_independent_nodes_idx - begin_independent_nodes_idx; + remaining_nodes.resize(begin_independent_nodes_idx); // unsigned maxdegree = 0; // unsigned avgdegree = 0; // unsigned mindegree = UINT_MAX; @@ -551,25 +611,37 @@ class Contractor // quad: " << quaddegree; p.printStatus(number_of_contracted_nodes); + ++current_level; } if (remaining_nodes.size() > 2) { - if (orig_node_id_from_new_node_id_map.empty()) - { - for (const auto &node : remaining_nodes) - { - is_core_node[node.id] = true; - } - } - else - { - for (const auto &node : remaining_nodes) + if (orig_node_id_from_new_node_id_map.size() > 0) { - auto orig_id = orig_node_id_from_new_node_id_map[node.id]; - is_core_node[orig_id] = true; + tbb::parallel_for( + tbb::blocked_range(0, remaining_nodes.size(), InitGrainSize), + [this, &remaining_nodes](const tbb::blocked_range &range) + { + for (int x = range.begin(), end = range.end(); x != end; ++x) + { + const auto orig_id = remaining_nodes[x].id; + is_core_node[orig_node_id_from_new_node_id_map[orig_id]] = true; + } + }); + } + else + { + tbb::parallel_for( + tbb::blocked_range(0, remaining_nodes.size(), InitGrainSize), + [this, &remaining_nodes](const tbb::blocked_range &range) + { + for (int x = range.begin(), end = range.end(); x != end; ++x) + { + const auto orig_id = remaining_nodes[x].id; + is_core_node[orig_id] = true; + } + }); } - } } else { @@ -589,6 +661,11 @@ class Contractor out_is_core_node.swap(is_core_node); } + inline void GetNodeLevels(std::vector &out_node_levels) + { + out_node_levels.swap(node_levels); + } + template inline void GetEdges(DeallocatingVector &edges) { Percent p(contractor_graph->GetNumberOfNodes()); @@ -998,6 +1075,7 @@ class Contractor std::shared_ptr contractor_graph; stxxl::vector external_edge_list; std::vector orig_node_id_from_new_node_id_map; + std::vector node_levels; std::vector is_core_node; XORFastHash fast_hash; }; diff --git a/contractor/contractor_options.cpp b/contractor/contractor_options.cpp index ff22bb6ff..c97804858 100644 --- a/contractor/contractor_options.cpp +++ b/contractor/contractor_options.cpp @@ -48,16 +48,20 @@ ContractorOptions::ParseArguments(int argc, char *argv[], ContractorConfig &cont // declare a group of options that will be allowed both on command line and in config file boost::program_options::options_description config_options("Configuration"); config_options.add_options()( - "profile,p", boost::program_options::value(&contractor_config.profile_path) - ->default_value("profile.lua"), + "profile,p", + boost::program_options::value(&contractor_config.profile_path) + ->default_value("profile.lua"), "Path to LUA routing profile")( - "threads,t", boost::program_options::value(&contractor_config.requested_num_threads) - ->default_value(tbb::task_scheduler_init::default_num_threads()), + "threads,t", + boost::program_options::value(&contractor_config.requested_num_threads) + ->default_value(tbb::task_scheduler_init::default_num_threads()), "Number of threads to use")( - "core,k", boost::program_options::value(&contractor_config.core_factor) - ->default_value(1.0),"Percentage of the graph (in vertices) to contract [0.1]"); - - + "core,k", + boost::program_options::value(&contractor_config.core_factor)->default_value(1.0), + "Percentage of the graph (in vertices) to contract [0.1]")( + "level-cache,o", + boost::program_options::value(&contractor_config.use_cached_priority)->default_value(false), + "Use .level file to retain the contaction level for each node from the last run."); // hidden options, will be allowed both on command line and in config file, but will not be // shown to the user @@ -122,6 +126,7 @@ ContractorOptions::ParseArguments(int argc, char *argv[], ContractorConfig &cont void ContractorOptions::GenerateOutputFilesNames(ContractorConfig &contractor_config) { + contractor_config.level_output_path = contractor_config.osrm_input_path.string() + ".level"; contractor_config.core_output_path = contractor_config.osrm_input_path.string() + ".core"; contractor_config.graph_output_path = contractor_config.osrm_input_path.string() + ".hsgr"; contractor_config.edge_based_graph_path = contractor_config.osrm_input_path.string() + ".ebg"; diff --git a/contractor/contractor_options.hpp b/contractor/contractor_options.hpp index 8ad2363e3..d68757cee 100644 --- a/contractor/contractor_options.hpp +++ b/contractor/contractor_options.hpp @@ -47,10 +47,13 @@ struct ContractorConfig boost::filesystem::path osrm_input_path; boost::filesystem::path profile_path; + std::string level_output_path; std::string core_output_path; std::string graph_output_path; std::string edge_based_graph_path; + bool use_cached_priority; + unsigned requested_num_threads; //A percentage of vertices that will be contracted for the hierarchy. diff --git a/contractor/processing_chain.cpp b/contractor/processing_chain.cpp index 2aeae4837..435c3e761 100644 --- a/contractor/processing_chain.cpp +++ b/contractor/processing_chain.cpp @@ -26,8 +26,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "processing_chain.hpp" - #include "contractor.hpp" + #include "../data_structures/deallocating_vector.hpp" #include "../algorithms/crc32_processor.hpp" @@ -78,8 +78,13 @@ int Prepare::Run() TIMER_START(contraction); std::vector is_core_node; + std::vector node_levels; + if (config.use_cached_priority) + { + ReadNodeLevels(node_levels); + } DeallocatingVector contracted_edge_list; - ContractGraph(max_edge_id, edge_based_edge_list, contracted_edge_list, is_core_node); + ContractGraph(max_edge_id, edge_based_edge_list, contracted_edge_list, is_core_node, node_levels); TIMER_STOP(contraction); SimpleLogger().Write() << "Contraction took " << TIMER_SEC(contraction) << " sec"; @@ -87,6 +92,10 @@ int Prepare::Run() std::size_t number_of_used_edges = WriteContractedGraph(max_edge_id, contracted_edge_list); WriteCoreNodeMarker(std::move(is_core_node)); + if (!config.use_cached_priority) + { + WriteNodeLevels(std::move(node_levels)); + } TIMER_STOP(preparing); @@ -131,11 +140,35 @@ std::size_t Prepare::LoadEdgeExpandedGraph( return max_edge_id; } +void Prepare::ReadNodeLevels(std::vector &node_levels) const +{ + boost::filesystem::ifstream order_input_stream(config.level_output_path, + std::ios::binary); + + unsigned level_size; + order_input_stream.read((char *)&level_size, sizeof(unsigned)); + node_levels.resize(level_size); + order_input_stream.read((char *)node_levels.data(), + sizeof(float) * node_levels.size()); +} + +void Prepare::WriteNodeLevels(std::vector &&in_node_levels) const +{ + std::vector node_levels(std::move(in_node_levels)); + + boost::filesystem::ofstream order_output_stream(config.level_output_path, + std::ios::binary); + + unsigned level_size = node_levels.size(); + order_output_stream.write((char *)&level_size, sizeof(unsigned)); + order_output_stream.write((char *)node_levels.data(), + sizeof(float) * node_levels.size()); +} void Prepare::WriteCoreNodeMarker(std::vector &&in_is_core_node) const { - std::vector is_core_node(in_is_core_node); - std::vector unpacked_bool_flags(is_core_node.size()); + std::vector is_core_node(std::move(in_is_core_node)); + std::vector unpacked_bool_flags(std::move(is_core_node.size())); for (auto i = 0u; i < is_core_node.size(); ++i) { unpacked_bool_flags[i] = is_core_node[i] ? 1 : 0; @@ -270,12 +303,17 @@ std::size_t Prepare::WriteContractedGraph(unsigned max_node_id, void Prepare::ContractGraph(const unsigned max_edge_id, DeallocatingVector &edge_based_edge_list, DeallocatingVector &contracted_edge_list, - std::vector &is_core_node) + std::vector &is_core_node, + std::vector &inout_node_levels) const { - Contractor contractor(max_edge_id + 1, edge_based_edge_list); + std::vector node_levels; + node_levels.swap(inout_node_levels); + + Contractor contractor(max_edge_id + 1, edge_based_edge_list, std::move(node_levels)); contractor.Run(config.core_factor); contractor.GetEdges(contracted_edge_list); contractor.GetCoreMarker(is_core_node); + contractor.GetNodeLevels(inout_node_levels); } diff --git a/contractor/processing_chain.hpp b/contractor/processing_chain.hpp index 1743cc880..4e97ce153 100644 --- a/contractor/processing_chain.hpp +++ b/contractor/processing_chain.hpp @@ -60,8 +60,11 @@ class Prepare void ContractGraph(const unsigned max_edge_id, DeallocatingVector &edge_based_edge_list, DeallocatingVector &contracted_edge_list, - std::vector &is_core_node); + std::vector &is_core_node, + std::vector &node_levels) const; void WriteCoreNodeMarker(std::vector &&is_core_node) const; + void WriteNodeLevels(std::vector &&node_levels) const; + void ReadNodeLevels(std::vector &contraction_order) const; std::size_t WriteContractedGraph(unsigned number_of_edge_based_nodes, const DeallocatingVector &contracted_edge_list); void FindComponents(unsigned max_edge_id,