Port Contractor to TBB
This commit is contained in:
		
							parent
							
								
									77641a9fce
								
							
						
					
					
						commit
						f487845e9d
					
				| @ -179,6 +179,7 @@ target_link_libraries(osrm-extract ${CMAKE_THREAD_LIBS_INIT}) | ||||
| 
 | ||||
| find_package(TBB REQUIRED) | ||||
| target_link_libraries(osrm-extract ${TBB_LIBRARIES}) | ||||
| target_link_libraries(osrm-prepare ${TBB_LIBRARIES}) | ||||
| include_directories(${TBB_INCLUDE_DIR}) | ||||
| 
 | ||||
| find_package(Lua52) | ||||
|  | ||||
| @ -38,9 +38,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| #include "../Util/OpenMPWrapper.h" | ||||
| #include "../Util/SimpleLogger.h" | ||||
| #include "../Util/StringUtil.h" | ||||
| #include "../Util/TimingUtil.h" | ||||
| 
 | ||||
| #include <boost/assert.hpp> | ||||
| 
 | ||||
| #include <tbb/enumerable_thread_specific.h> | ||||
| #include <tbb/parallel_for.h> | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <limits> | ||||
| #include <vector> | ||||
| @ -125,6 +129,28 @@ class Contractor | ||||
|         bool is_independent : 1; | ||||
|     }; | ||||
| 
 | ||||
| 
 | ||||
|     struct ThreadDataContainer | ||||
|     { | ||||
|         ThreadDataContainer(int number_of_nodes) : number_of_nodes(number_of_nodes)  {} | ||||
| 
 | ||||
|         inline ContractorThreadData* getThreadData() | ||||
|         { | ||||
|             bool exists = false; | ||||
|             auto& ref = data.local(exists); | ||||
|             if (!exists) | ||||
|             { | ||||
|                 ref = std::make_shared<ContractorThreadData>(number_of_nodes); | ||||
|             } | ||||
| 
 | ||||
|             return ref.get(); | ||||
|         } | ||||
| 
 | ||||
|         int number_of_nodes; | ||||
|         typedef tbb::enumerable_thread_specific<std::shared_ptr<ContractorThreadData>> EnumerableThreadData; | ||||
|         EnumerableThreadData data; | ||||
|     }; | ||||
| 
 | ||||
|   public: | ||||
|     template <class ContainerT> Contractor(int nodes, ContainerT &input_edge_list) | ||||
|     { | ||||
| @ -262,39 +288,51 @@ class Contractor | ||||
| 
 | ||||
|     void Run() | ||||
|     { | ||||
|         // for the preperation we can use a big grain size, which is much faster (probably cache)
 | ||||
|         constexpr size_t InitGrainSize        = 100000; | ||||
|         constexpr size_t PQGrainSize          = 100000; | ||||
|         // auto_partitioner will automatically increase the blocksize if we have
 | ||||
|         // a lot of data. It is *important* for the last loop iterations
 | ||||
|         // (which have a very small dataset) that it is devisible.
 | ||||
|         constexpr size_t IndependentGrainSize = 1; | ||||
|         constexpr size_t ContractGrainSize    = 1; | ||||
|         constexpr size_t NeighboursGrainSize  = 1; | ||||
|         constexpr size_t DeleteGrainSize      = 1; | ||||
| 
 | ||||
|         const NodeID number_of_nodes = contractor_graph->GetNumberOfNodes(); | ||||
|         Percent p(number_of_nodes); | ||||
| 
 | ||||
|         const unsigned thread_count = omp_get_max_threads(); | ||||
|         std::vector<ContractorThreadData *> thread_data_list; | ||||
|         for (unsigned thread_id = 0; thread_id < thread_count; ++thread_id) | ||||
|         { | ||||
|             thread_data_list.push_back(new ContractorThreadData(number_of_nodes)); | ||||
|         } | ||||
|         std::cout << "Contractor is using " << thread_count << " threads" << std::endl; | ||||
|         ThreadDataContainer thread_data_list(number_of_nodes); | ||||
| 
 | ||||
|         NodeID number_of_contracted_nodes = 0; | ||||
|         std::vector<RemainingNodeData> remaining_nodes(number_of_nodes); | ||||
|         std::vector<float> node_priorities(number_of_nodes); | ||||
|         std::vector<NodePriorityData> node_data(number_of_nodes); | ||||
| 
 | ||||
| // initialize priorities in parallel
 | ||||
| #pragma omp parallel for schedule(guided) | ||||
|         for (int x = 0; x < (int)number_of_nodes; ++x) | ||||
|         { | ||||
|             remaining_nodes[x].id = x; | ||||
|         } | ||||
| 
 | ||||
|         // initialize priorities in parallel
 | ||||
|         tbb::parallel_for(tbb::blocked_range<int>(0, number_of_nodes, InitGrainSize), | ||||
|             [&remaining_nodes](const tbb::blocked_range<int>& range) | ||||
|             { | ||||
|                 for (int x = range.begin(); x != range.end(); ++x) | ||||
|                 { | ||||
|                     remaining_nodes[x].id = x; | ||||
|                 } | ||||
|             } | ||||
|         ); | ||||
| 
 | ||||
| 
 | ||||
|         std::cout << "initializing elimination PQ ..." << std::flush; | ||||
| #pragma omp parallel | ||||
|         { | ||||
|             ContractorThreadData *data = thread_data_list[omp_get_thread_num()]; | ||||
| #pragma omp parallel for schedule(guided) | ||||
|             for (int x = 0; x < (int)number_of_nodes; ++x) | ||||
|         tbb::parallel_for(tbb::blocked_range<int>(0, number_of_nodes, PQGrainSize), | ||||
|             [this, &node_priorities, &node_data, &thread_data_list](const tbb::blocked_range<int>& range) | ||||
|             { | ||||
|                 node_priorities[x] = EvaluateNodePriority(data, &node_data[x], x); | ||||
|                 ContractorThreadData *data = thread_data_list.getThreadData(); | ||||
|                 for (int x = range.begin(); x != range.end(); ++x) | ||||
|                 { | ||||
|                     node_priorities[x] = this->EvaluateNodePriority(data, &node_data[x], x); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         ); | ||||
|         std::cout << "ok" << std::endl << "preprocessing " << number_of_nodes << " nodes ..." | ||||
|                   << std::flush; | ||||
| 
 | ||||
| @ -309,11 +347,7 @@ class Contractor | ||||
|                 std::cout << " [flush " << number_of_contracted_nodes << " nodes] " << std::flush; | ||||
| 
 | ||||
|                 // Delete old heap data to free memory that we need for the coming operations
 | ||||
|                 for (ContractorThreadData *data : thread_data_list) | ||||
|                 { | ||||
|                     delete data; | ||||
|                 } | ||||
|                 thread_data_list.clear(); | ||||
|                 thread_data_list.data.clear(); | ||||
| 
 | ||||
|                 // Create new priority array
 | ||||
|                 std::vector<float> new_node_priority(remaining_nodes.size()); | ||||
| @ -396,59 +430,67 @@ class Contractor | ||||
| 
 | ||||
|                 // INFO: MAKE SURE THIS IS THE LAST OPERATION OF THE FLUSH!
 | ||||
|                 // reinitialize heaps and ThreadData objects with appropriate size
 | ||||
|                 for (unsigned thread_id = 0; thread_id < thread_count; ++thread_id) | ||||
|                 { | ||||
|                     thread_data_list.push_back( | ||||
|                         new ContractorThreadData(contractor_graph->GetNumberOfNodes())); | ||||
|                 } | ||||
|                 thread_data_list.number_of_nodes = contractor_graph->GetNumberOfNodes(); | ||||
|             } | ||||
| 
 | ||||
|             const int last = (int)remaining_nodes.size(); | ||||
| #pragma omp parallel | ||||
|             { | ||||
|                 // determine independent node set
 | ||||
|                 ContractorThreadData *const data = thread_data_list[omp_get_thread_num()]; | ||||
| #pragma omp for schedule(guided) | ||||
|                 for (int i = 0; i < last; ++i) | ||||
|             tbb::parallel_for(tbb::blocked_range<int>(0, last, IndependentGrainSize), | ||||
|                 [this, &node_priorities, &remaining_nodes, &thread_data_list](const tbb::blocked_range<int>& range) | ||||
|                 { | ||||
|                     const NodeID node = remaining_nodes[i].id; | ||||
|                     remaining_nodes[i].is_independent = | ||||
|                         IsNodeIndependent(node_priorities, data, node); | ||||
|                     ContractorThreadData *data = thread_data_list.getThreadData(); | ||||
|                     // determine independent node set
 | ||||
|                     for (int i = range.begin(); i != range.end(); ++i) | ||||
|                     { | ||||
|                         const NodeID node = remaining_nodes[i].id; | ||||
|                         remaining_nodes[i].is_independent = | ||||
|                             this->IsNodeIndependent(node_priorities, data, node); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             ); | ||||
| 
 | ||||
|             const auto first = stable_partition(remaining_nodes.begin(), | ||||
|                                                 remaining_nodes.end(), | ||||
|                                                 [](RemainingNodeData node_data) | ||||
|                                                 { return !node_data.is_independent; }); | ||||
|             const int first_independent_node = first - remaining_nodes.begin(); | ||||
| // contract independent nodes
 | ||||
| #pragma omp parallel | ||||
|             { | ||||
|                 ContractorThreadData *data = thread_data_list[omp_get_thread_num()]; | ||||
| #pragma omp for schedule(guided) nowait | ||||
|                 for (int position = first_independent_node; position < last; ++position) | ||||
|                 { | ||||
|                     NodeID x = remaining_nodes[position].id; | ||||
|                     ContractNode<false>(data, x); | ||||
|                 } | ||||
| 
 | ||||
|                 std::sort(data->inserted_edges.begin(), data->inserted_edges.end()); | ||||
|             } | ||||
| #pragma omp parallel | ||||
|             { | ||||
|                 ContractorThreadData *data = thread_data_list[omp_get_thread_num()]; | ||||
| #pragma omp for schedule(guided) nowait | ||||
|                 for (int position = first_independent_node; position < last; ++position) | ||||
|             // contract independent nodes
 | ||||
|             tbb::parallel_for(tbb::blocked_range<int>(first_independent_node, last, ContractGrainSize), | ||||
|                 [this, &remaining_nodes, &thread_data_list](const tbb::blocked_range<int>& range) | ||||
|                 { | ||||
|                     NodeID x = remaining_nodes[position].id; | ||||
|                     DeleteIncomingEdges(data, x); | ||||
|                     ContractorThreadData *data = thread_data_list.getThreadData(); | ||||
|                     for (int position = range.begin(); position != range.end(); ++position) | ||||
|                     { | ||||
|                         NodeID x = remaining_nodes[position].id; | ||||
|                         this->ContractNode<false>(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) | ||||
|                         std::sort(data->inserted_edges.begin(), | ||||
|                                   data->inserted_edges.end()); | ||||
|                 } | ||||
|             ); | ||||
|             tbb::parallel_for(tbb::blocked_range<int>(first_independent_node, last, DeleteGrainSize), | ||||
|                 [this, &remaining_nodes, &thread_data_list](const tbb::blocked_range<int>& range) | ||||
|                 { | ||||
|                     ContractorThreadData *data = thread_data_list.getThreadData(); | ||||
|                     for (int position = range.begin(); position != range.end(); ++position) | ||||
|                     { | ||||
|                         NodeID x = remaining_nodes[position].id; | ||||
|                         this->DeleteIncomingEdges(data, x); | ||||
|                     } | ||||
|                 } | ||||
|             ); | ||||
| 
 | ||||
|             // insert new edges
 | ||||
|             for (unsigned thread_id = 0; thread_id < thread_count; ++thread_id) | ||||
|             for (auto& data : thread_data_list.data) | ||||
|             { | ||||
|                 ContractorThreadData &data = *thread_data_list[thread_id]; | ||||
|                 for (const ContractorEdge &edge : data.inserted_edges) | ||||
|                 for (const ContractorEdge &edge : data->inserted_edges) | ||||
|                 { | ||||
|                     auto current_edge_ID = contractor_graph->FindEdge(edge.source, edge.target); | ||||
|                     if (current_edge_ID < contractor_graph->EndEdges(edge.source)) | ||||
| @ -466,19 +508,21 @@ class Contractor | ||||
|                     } | ||||
|                     contractor_graph->InsertEdge(edge.source, edge.target, edge.data); | ||||
|                 } | ||||
|                 data.inserted_edges.clear(); | ||||
|                 data->inserted_edges.clear(); | ||||
|             } | ||||
| // update priorities
 | ||||
| #pragma omp parallel | ||||
|             { | ||||
|                 ContractorThreadData *data = thread_data_list[omp_get_thread_num()]; | ||||
| #pragma omp for schedule(guided) nowait | ||||
|                 for (int position = first_independent_node; position < last; ++position) | ||||
| 
 | ||||
|             tbb::parallel_for(tbb::blocked_range<int>(first_independent_node, last, NeighboursGrainSize), | ||||
|                 [this, &remaining_nodes, &node_priorities, &node_data, &thread_data_list](const tbb::blocked_range<int>& range) | ||||
|                 { | ||||
|                     NodeID x = remaining_nodes[position].id; | ||||
|                     UpdateNodeNeighbours(node_priorities, node_data, data, x); | ||||
|                     ContractorThreadData *data = thread_data_list.getThreadData(); | ||||
|                     for (int position = range.begin(); position != range.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); | ||||
| @ -510,11 +554,8 @@ class Contractor | ||||
| 
 | ||||
|             p.printStatus(number_of_contracted_nodes); | ||||
|         } | ||||
|         for (ContractorThreadData *data : thread_data_list) | ||||
|         { | ||||
|             delete data; | ||||
|         } | ||||
|         thread_data_list.clear(); | ||||
| 
 | ||||
|         thread_data_list.data.clear(); | ||||
|     } | ||||
| 
 | ||||
|     template <class Edge> inline void GetEdges(DeallocatingVector<Edge> &edges) | ||||
| @ -769,7 +810,6 @@ class Contractor | ||||
|                                                true, | ||||
|                                                true, | ||||
|                                                false); | ||||
|                         ; | ||||
|                         inserted_edges.push_back(new_edge); | ||||
|                         std::swap(new_edge.source, new_edge.target); | ||||
|                         new_edge.data.forward = false; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user