diff --git a/coding/file_reader.hpp b/coding/file_reader.hpp index db7f449a4e0..52f0e4aa931 100644 --- a/coding/file_reader.hpp +++ b/coding/file_reader.hpp @@ -10,7 +10,7 @@ #include // FileReader, cheap to copy, not thread safe. -// It is assumed that file is not modified during FireReader lifetime, +// It is assumed that file is not modified during FileReader lifetime, // because of caching and assumption that Size() is constant. class FileReader : public ModelReader { diff --git a/generator/generator_tests/altitude_test.cpp b/generator/generator_tests/altitude_test.cpp index fe4235859e7..15ae72b4d11 100644 --- a/generator/generator_tests/altitude_test.cpp +++ b/generator/generator_tests/altitude_test.cpp @@ -134,13 +134,13 @@ void TestAltitudes(DataSource const & dataSource, MwmSet::MwmId const & mwmId, std::string const & mwmPath, bool hasAltitudeExpected, AltitudeGetter & expectedAltitudes) { - AltitudeLoader loader(dataSource, mwmId); + AltitudeLoader loader(dataSource, mwmId, false /* twoThreadsReady */); TEST_EQUAL(loader.HasAltitudes(), hasAltitudeExpected, ()); auto processor = [&expectedAltitudes, &loader](FeatureType & f, uint32_t const & id) { f.ParseGeometry(FeatureType::BEST_GEOMETRY); size_t const pointsCount = f.GetPointsCount(); - geometry::Altitudes const altitudes = loader.GetAltitudes(id, pointsCount); + geometry::Altitudes const altitudes = loader.GetAltitudes(id, pointsCount, true /* isOutgoing */); if (!routing::IsRoad(feature::TypesHolder(f))) { diff --git a/generator/generator_tests_support/routing_helpers.cpp b/generator/generator_tests_support/routing_helpers.cpp index a2bbf5fff49..2a1424694d0 100644 --- a/generator/generator_tests_support/routing_helpers.cpp +++ b/generator/generator_tests_support/routing_helpers.cpp @@ -46,8 +46,9 @@ void ReEncodeOsmIdsToFeatureIdsMapping(std::string const & mappingContent, std:: namespace routing { -void TestGeometryLoader::Load(uint32_t featureId, RoadGeometry & road) +void TestGeometryLoader::Load(uint32_t featureId, RoadGeometry & road, bool isOutgoing) { + CHECK(isOutgoing, ("TestGeometryLoader() is not ready for two threads feature parsing.")); auto const it = m_roads.find(featureId); if (it == m_roads.cend()) return; @@ -98,7 +99,7 @@ std::unique_ptr BuildIndexGraph(std::unique_ptr std::vector const & joints) { auto graph = std::make_unique(std::make_shared(std::move(geometryLoader)), - estimator); + estimator); graph->Import(joints); return graph; } diff --git a/generator/generator_tests_support/routing_helpers.hpp b/generator/generator_tests_support/routing_helpers.hpp index bc5387d0704..baf98a5724c 100644 --- a/generator/generator_tests_support/routing_helpers.hpp +++ b/generator/generator_tests_support/routing_helpers.hpp @@ -31,7 +31,7 @@ class TestGeometryLoader : public GeometryLoader { public: // GeometryLoader overrides: - void Load(uint32_t featureId, routing::RoadGeometry & road) override; + void Load(uint32_t featureId, routing::RoadGeometry & road, bool isOutgoing) override; void AddRoad(uint32_t featureId, bool oneWay, float speed, routing::RoadGeometry::Points const & points); diff --git a/generator/restriction_collector.cpp b/generator/restriction_collector.cpp index d02c02b4bae..1fe59b28b95 100644 --- a/generator/restriction_collector.cpp +++ b/generator/restriction_collector.cpp @@ -129,8 +129,10 @@ bool RestrictionCollector::ParseRestrictions(std::string const & path) Joint::Id RestrictionCollector::GetFirstCommonJoint(uint32_t firstFeatureId, uint32_t secondFeatureId) const { - uint32_t const firstLen = m_indexGraph->GetGeometry().GetRoad(firstFeatureId).GetPointsCount(); - uint32_t const secondLen = m_indexGraph->GetGeometry().GetRoad(secondFeatureId).GetPointsCount(); + uint32_t const firstLen = + m_indexGraph->GetGeometry().GetRoad(firstFeatureId, true /* isOutgoing */).GetPointsCount(); + uint32_t const secondLen = + m_indexGraph->GetGeometry().GetRoad(secondFeatureId, true /* isOutgoing */).GetPointsCount(); auto const firstRoad = m_indexGraph->GetRoad(firstFeatureId); auto const secondRoad = m_indexGraph->GetRoad(secondFeatureId); @@ -155,7 +157,7 @@ bool RestrictionCollector::FeatureHasPointWithCoords(uint32_t featureId, m2::PointD const & coords) const { CHECK(m_indexGraph, ()); - auto const & roadGeometry = m_indexGraph->GetGeometry().GetRoad(featureId); + auto const & roadGeometry = m_indexGraph->GetGeometry().GetRoad(featureId, true /* isOutgoing */); uint32_t const pointsCount = roadGeometry.GetPointsCount(); for (uint32_t i = 0; i < pointsCount; ++i) { @@ -245,7 +247,7 @@ bool RestrictionCollector::CheckAndProcessUTurn(Restriction::Type & restrictionT uint32_t & featureId = featureIds.back(); - auto const & road = m_indexGraph->GetGeometry().GetRoad(featureId); + auto const & road = m_indexGraph->GetGeometry().GetRoad(featureId, true /* isOutgoing */); // Can not do UTurn from feature to the same feature if it is one way. if (road.IsOneWay()) return false; diff --git a/generator/routing_index_generator.cpp b/generator/routing_index_generator.cpp index 21ba8d5e487..49eb156cf85 100644 --- a/generator/routing_index_generator.cpp +++ b/generator/routing_index_generator.cpp @@ -173,7 +173,7 @@ class IndexGraphWrapper final routing::Segment GetFinishSegment() const { return {}; } bool ConvertToReal(routing::Segment const & /* segment */) const { return false; } routing::RouteWeight HeuristicCostEstimate(routing::Segment const & /* from */, - ms::LatLon const & /* to */) + ms::LatLon const & /* to */, bool /* isOutgoing */) { CHECK(false, ("This method exists only for compatibility with IndexGraphStarterJoints")); return routing::GetAStarWeightZero(); @@ -200,11 +200,12 @@ class IndexGraphWrapper final } routing::RouteWeight GetAStarWeightEpsilon() { return routing::RouteWeight(0.0); } + bool IsTwoThreadsReady() const { return false; } // @} - ms::LatLon const & GetPoint(routing::Segment const & s, bool forward) + ms::LatLon const & GetPoint(routing::Segment const & s, bool forward, bool isOutgoing) { - return m_graph.GetPoint(s, forward); + return m_graph.GetPoint(s, forward, isOutgoing); } void GetEdgesList(routing::Segment const & child, bool isOutgoing, @@ -223,14 +224,14 @@ class IndexGraphWrapper final *m_AStarParents); } - bool IsJoint(routing::Segment const & segment, bool fromStart) const + bool IsJoint(routing::Segment const & segment, bool fromStart, bool isOutgoing) const { - return IsJointOrEnd(segment, fromStart); + return IsJointOrEnd(segment, fromStart, isOutgoing); } - bool IsJointOrEnd(routing::Segment const & segment, bool fromStart) const + bool IsJointOrEnd(routing::Segment const & segment, bool fromStart, bool isOutgoing) const { - return m_graph.IsJointOrEnd(segment, fromStart); + return m_graph.IsJointOrEnd(segment, fromStart, isOutgoing); } template @@ -256,7 +257,8 @@ class DijkstraWrapperJoints : public routing::IndexGraphStarterJoints(); } diff --git a/indexer/altitude_loader.cpp b/indexer/altitude_loader.cpp index 6db076926e2..48c1aa7406e 100644 --- a/indexer/altitude_loader.cpp +++ b/indexer/altitude_loader.cpp @@ -33,10 +33,13 @@ void LoadAndMap(size_t dataSize, ReaderSource & src, T namespace feature { -AltitudeLoader::AltitudeLoader(DataSource const & dataSource, MwmSet::MwmId const & mwmId) +AltitudeLoader::AltitudeLoader(DataSource const & dataSource, MwmSet::MwmId const & mwmId, + bool twoThreadsReady) : m_handle(dataSource.GetMwmHandleById(mwmId)) + , m_handleBwd(twoThreadsReady ? make_unique(dataSource.GetMwmHandleById(mwmId)) + : nullptr) { - if (!m_handle.IsAlive()) + if (!m_handle.IsAlive() || (IsTwoThreadsReady() && !m_handleBwd->IsAlive())) return; auto const & mwmValue = *m_handle.GetValue(); @@ -58,6 +61,12 @@ AltitudeLoader::AltitudeLoader(DataSource const & dataSource, MwmSet::MwmId cons LoadAndMap(m_header.GetAltitudeAvailabilitySize(), src, m_altitudeAvailability, m_altitudeAvailabilityRegion); LoadAndMap(m_header.GetFeatureTableSize(), src, m_featureTable, m_featureTableRegion); + + if (IsTwoThreadsReady()) + { + m_readerBwd = make_unique( + m_handleBwd->GetValue()->m_cont.GetReader(ALTITUDES_FILE_TAG)); + } } catch (Reader::OpenException const & e) { @@ -71,24 +80,36 @@ bool AltitudeLoader::HasAltitudes() const return m_reader != nullptr && m_header.m_minAltitude != geometry::kInvalidAltitude; } -geometry::Altitudes const & AltitudeLoader::GetAltitudes(uint32_t featureId, size_t pointCount) +geometry::Altitudes const & AltitudeLoader::GetAltitudes(uint32_t featureId, size_t pointCount, + bool isOutgoing) +{ + // Note. The backward reader and cache should not be used in case of calling GetAltitudes() + // method from one thread. + auto isForward = IsTwoThreadsReady() ? isOutgoing : true; + return GetAltitudes(featureId, pointCount, isForward ? m_reader : m_readerBwd, + isForward ? m_cache : m_cacheBwd); +} + +geometry::Altitudes const & AltitudeLoader::AltitudeLoader::GetAltitudes( + uint32_t featureId, size_t pointCount, unique_ptr & reader, + map & cache) const { if (!HasAltitudes()) { // There's no altitude section in mwm. - return m_cache + return cache .insert( make_pair(featureId, geometry::Altitudes(pointCount, geometry::kDefaultAltitudeMeters))) .first->second; } - auto const it = m_cache.find(featureId); - if (it != m_cache.end()) + auto const it = cache.find(featureId); + if (it != cache.end()) return it->second; if (!m_altitudeAvailability[featureId]) { - return m_cache + return cache .insert(make_pair(featureId, geometry::Altitudes(pointCount, m_header.m_minAltitude))) .first->second; } @@ -99,35 +120,35 @@ geometry::Altitudes const & AltitudeLoader::GetAltitudes(uint32_t featureId, siz CHECK_LESS_OR_EQUAL(offset, m_featureTable.size(), ("Feature Id", featureId, "of", m_countryFileName)); uint64_t const altitudeInfoOffsetInSection = m_header.m_altitudesOffset + offset; - CHECK_LESS(altitudeInfoOffsetInSection, m_reader->Size(), ("Feature Id", featureId, "of", m_countryFileName)); + CHECK_LESS(altitudeInfoOffsetInSection, reader->Size(), ("Feature Id", featureId, "of", m_countryFileName)); try { Altitudes altitudes; - ReaderSource src(*m_reader); + ReaderSource src(*reader); src.Skip(altitudeInfoOffsetInSection); bool const isDeserialized = altitudes.Deserialize(m_header.m_minAltitude, pointCount, m_countryFileName, featureId, src); bool const allValid = isDeserialized && - none_of(altitudes.m_altitudes.begin(), altitudes.m_altitudes.end(), - [](geometry::Altitude a) { return a == geometry::kInvalidAltitude; }); + none_of(altitudes.m_altitudes.begin(), altitudes.m_altitudes.end(), + [](geometry::Altitude a) { return a == geometry::kInvalidAltitude; }); if (!allValid) { - LOG(LERROR, ("Only a part point of a feature has a valid altitdue. Altitudes: ", altitudes.m_altitudes, - ". Feature Id", featureId, "of", m_countryFileName)); - return m_cache + LOG(LERROR, ("Only a part point of a feature has a valid altitude. Altitudes: ", altitudes.m_altitudes, + ". Feature Id", featureId, "of", m_countryFileName)); + return cache .insert(make_pair(featureId, geometry::Altitudes(pointCount, m_header.m_minAltitude))) .first->second; } - return m_cache.insert(make_pair(featureId, move(altitudes.m_altitudes))).first->second; + return cache.insert(make_pair(featureId, move(altitudes.m_altitudes))).first->second; } catch (Reader::OpenException const & e) { LOG(LERROR, ("Feature Id", featureId, "of", m_countryFileName, ". Error while getting altitude data:", e.Msg())); - return m_cache + return cache .insert(make_pair(featureId, geometry::Altitudes(pointCount, m_header.m_minAltitude))) .first->second; } diff --git a/indexer/altitude_loader.hpp b/indexer/altitude_loader.hpp index dee9285e34e..1a1988baf86 100644 --- a/indexer/altitude_loader.hpp +++ b/indexer/altitude_loader.hpp @@ -25,27 +25,43 @@ namespace feature class AltitudeLoader { public: - AltitudeLoader(DataSource const & dataSource, MwmSet::MwmId const & mwmId); + AltitudeLoader(DataSource const & dataSource, MwmSet::MwmId const & mwmId, + bool twoThreadsReady); + + bool HasAltitudes() const; /// \returns altitude of feature with |featureId|. All items of the returned vector are valid /// or the returned vector is empty. - geometry::Altitudes const & GetAltitudes(uint32_t featureId, size_t pointCount); + geometry::Altitudes const & GetAltitudes(uint32_t featureId, size_t pointCount, bool isOutgoing); - bool HasAltitudes() const; + bool IsTwoThreadsReady() const { return m_handleBwd != nullptr; } - void ClearCache() { m_cache.clear(); } + void ClearCache(bool isOutgoing) { isOutgoing ? m_cache.clear() : m_cacheBwd.clear(); } private: + geometry::Altitudes const & GetAltitudes( + uint32_t featureId, size_t pointCount, std::unique_ptr & reader, + std::map & cache) const; + std::unique_ptr m_altitudeAvailabilityRegion; std::unique_ptr m_featureTableRegion; succinct::rs_bit_vector m_altitudeAvailability; succinct::elias_fano m_featureTable; + // Note. If |twoThreadsReady| parameter of constructor is true method GetAltitudes() may be called + // from two different threads. In that case all calls of GetAltitudes() with |isOutgoing| == true + // should be done from one thread and from another one of |isOutgoing| == true. + // To call GetAltitudes() from two threads without locks it's necessary to have + // two caches for reading from disk (in m_handle/m_reader and m_handleBwd/m_readerBwd) + // and two caches for keeping read altitudes (m_cache and m_cacheBwd). std::unique_ptr m_reader; + std::unique_ptr m_readerBwd; std::map m_cache; + std::map m_cacheBwd; AltitudeHeader m_header; std::string m_countryFileName; MwmSet::MwmHandle m_handle; + std::unique_ptr m_handleBwd; }; } // namespace feature diff --git a/routing/async_router.cpp b/routing/async_router.cpp index adc47afb133..c6f06627104 100644 --- a/routing/async_router.cpp +++ b/routing/async_router.cpp @@ -398,7 +398,7 @@ void AsyncRouter::CalculateRoute() absentFetcher->GenerateRequest(checkpoints); // Run basic request. - code = router->CalculateRoute(checkpoints, startDirection, adjustToPrevRoute, + code = router->CalculateRoute(checkpoints, startDirection, true /* useTwoThreads */, adjustToPrevRoute, delegateProxy->GetDelegate(), *route); router->SetGuides({}); elapsedSec = timer.ElapsedSeconds(); // routing time diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index 4ea7fa74633..0595f9cd689 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -8,11 +8,15 @@ #include "base/assert.hpp" #include "base/cancellable.hpp" #include "base/logging.hpp" +#include "base/optional_lock_guard.hpp" +#include "base/thread.hpp" #include +#include #include #include #include +#include #include #include #include @@ -28,13 +32,14 @@ namespace astar template struct DefaultVisitor { - void operator()(Vertex const & /* from */, Vertex const & /* to */) const {}; + void operator()(Vertex const & /* from */, Vertex const & /* to */, bool /* isOutgoing */, + std::optional & /* m */) const {}; }; template struct DefaultLengthChecker { - bool operator()(Weight const & /* weight */) const { return true; } + bool operator()(Weight const & /* weight */, bool /* isOutgoing */) const { return true; } }; } // namespace astar @@ -209,14 +214,23 @@ class AStarAlgorithm template Result FindPath(P & params, RoutingResult & result) const; + /// \brief Finds path on |params.m_graph| using bidirectional A* algorithm. + /// \note Two thread version is used when the version is set in |params.m_graph|. + /// If |params.m_graph.IsTwoThreadsReady()| returns false, one thread version is used. + /// It's worth using one thread version if there's only one core available. + /// if |params.m_graph.IsTwoThreadsReady()| returns true two thread version is used. + /// If the decision is made to use two thread version it should be taken into account: + /// - |isOutgoing| flag in each method specified which thread calls the method + /// - All callbacks which are called from |wave| lambda such as |params.m_onVisitedVertexCallback| + /// or |params.m_checkLengthCallback| should be ready for calling from two threads. template Result FindPathBidirectional(P & params, RoutingResult & result) const; // Adjust route to the previous one. // Expects |params.m_checkLengthCallback| to check wave propagation limit. template - typename AStarAlgorithm::Result AdjustRoute(P & params, - RoutingResult & result) const; + typename AStarAlgorithm::Result AdjustRoute( + P & params, RoutingResult & result) const; private: // Periodicity of switching a wave of bidirectional algorithm. @@ -267,12 +281,9 @@ class AStarAlgorithm { using Parents = typename Graph::Parents; - BidirectionalStepContext(bool forward, Vertex const & startVertex, Vertex const & finalVertex, - Graph & graph) - : forward(forward) - , startVertex(startVertex) - , finalVertex(finalVertex) - , graph(graph) + BidirectionalStepContext(std::optional & m, bool forward, + Vertex const & startVertex, Vertex const & finalVertex, Graph & graph) + : mtx(m), forward(forward), startVertex(startVertex), finalVertex(finalVertex), graph(graph) { bestVertex = forward ? startVertex : finalVertex; pS = ConsistentHeuristic(bestVertex); @@ -284,10 +295,17 @@ class AStarAlgorithm graph.DropAStarParents(); } + void Init(Vertex const & endVertex) + { + UpdateDistance(State(endVertex, kZeroDistance)); + queue.push(State(endVertex, kZeroDistance, ConsistentHeuristic(endVertex))); + } + Weight TopDistance() const { - ASSERT(!queue.empty(), ()); - return bestDistance.at(queue.top().vertex); + ASSERT(stateV || !queue.empty(), + ("Either stateV should have value or queue should have value(s).")); + return bestDistance.at(stateV ? stateV->vertex : queue.top().vertex); } // p_f(v) = 0.5*(π_f(v) - π_r(v)) @@ -303,8 +321,9 @@ class AStarAlgorithm // particular routes when debugging turned out to be easier. Weight ConsistentHeuristic(Vertex const & v) const { - auto const piF = graph.HeuristicCostEstimate(v, finalVertex); - auto const piR = graph.HeuristicCostEstimate(v, startVertex); + bool const isTwoThreads = IsTwoThreadsReady(); + auto const piF = graph.HeuristicCostEstimate(v, finalVertex, isTwoThreads ? forward : true); + auto const piR = graph.HeuristicCostEstimate(v, startVertex, isTwoThreads ? forward : true); if (forward) { /// @todo careful: with this "return" here and below in the Backward case @@ -321,17 +340,20 @@ class AStarAlgorithm bool ExistsStateWithBetterDistance(State const & state, Weight const & eps = Weight(0.0)) const { + base::OptionalLockGuard guard(mtx); auto const it = bestDistance.find(state.vertex); return it != bestDistance.end() && state.distance > it->second - eps; } void UpdateDistance(State const & state) { + base::OptionalLockGuard guard(mtx); bestDistance.insert_or_assign(state.vertex, state.distance); } std::optional GetDistance(Vertex const & vertex) const { + base::OptionalLockGuard guard(mtx); auto const it = bestDistance.find(vertex); return it != bestDistance.cend() ? std::optional(it->second) : std::nullopt; } @@ -341,7 +363,20 @@ class AStarAlgorithm parent.insert_or_assign(to, from); } - void GetAdjacencyList(State const & state, std::vector & adj) + void Update(State const & stateW, State const & stateV) + { + UpdateDistance(stateW); + UpdateParent(stateW.vertex, stateV.vertex); + } + + void UpdateAndPushIfNotEnd(State const & stateW, State const & stateV) + { + Update(stateW, stateV); + if (stateW.vertex != GetEndVertex()) + queue.push(stateW); + } + + void FillAdjacencyList(State const & state) { auto const realDistance = state.distance + pS - state.heuristic; astar::VertexData const data(state.vertex, realDistance); @@ -351,8 +386,25 @@ class AStarAlgorithm graph.GetIngoingEdgesList(data, adj); } + State TopAndPopState() + { + State state = stateV ? *stateV : queue.top(); + if (stateV) + stateV = std::nullopt; + else + queue.pop(); + return state; + } + Parents & GetParents() { return parent; } + Vertex const & GetEndVertex() const { return forward ? finalVertex : startVertex; } + + bool IsNextVertex() const {return !queue.empty() || stateV; } + + bool IsTwoThreadsReady() const { return mtx.has_value(); } + + std::optional & mtx; bool const forward; Vertex const & startVertex; Vertex const & finalVertex; @@ -362,8 +414,20 @@ class AStarAlgorithm ska::bytell_hash_map bestDistance; Parents parent; Vertex bestVertex; - Weight pS; + // |adj| is a parameter which is filled by FillAdjacencyList(). |adj| and |stateV| are necessary + // because IndexGraphStarterJoints (derived from AStarGraph) is not ready for + // a repeated call of IndexGraphStarterJoints::GetEdgeList() with the same |vertexData| and + // |isOutgoing| parameters. On the other hand after a call of AStarGraph::GetEdgeList() + // it may be seen that it's time to stop two-thread route building in FindPathBidirectional(), + // but the result of the call AStarGraph::GetEdgeList() (|adj|) and + // the vertex which the method AStarGraph::GetEdgeList() is called for (|stateV|), + // are still necessary for each context to finish route building in one thread step. + // So they should be kept to continue route building correctly after two thread step. + // If |stateV| has value it should be used instead of |queue.top()|. In that case + // |adj| is filled with outgoing or ingoing edges. If not, |queue.top()| should be used. + std::vector adj; + std::optional stateV; }; static void ReconstructPath(Vertex const & v, @@ -373,6 +437,12 @@ class AStarAlgorithm Vertex const & v, Vertex const & w, typename BidirectionalStepContext::Parents const & parentV, typename BidirectionalStepContext::Parents const & parentW, std::vector & path); + + template + void FindPathBidirectionalTwoThreadStep( + Weight const & epsilon, P & params, + AStarAlgorithm::BidirectionalStepContext & forward, + AStarAlgorithm::BidirectionalStepContext & backward) const; }; template @@ -482,8 +552,8 @@ AStarAlgorithm::FindPath(P & params, RoutingResult::FindPath(P & params, RoutingResult dummy; auto visitVertex = [&](Vertex const & vertex) { if (periodicCancellable.IsCancelled()) { @@ -503,7 +574,7 @@ AStarAlgorithm::FindPath(P & params, RoutingResult::FindPath(P & params, RoutingResult::FindPath(P & params, RoutingResult::FindPathBidirectional(P & params, auto const & finalVertex = params.m_finalVertex; auto const & startVertex = params.m_startVertex; - BidirectionalStepContext forward(true /* forward */, startVertex, finalVertex, graph); - BidirectionalStepContext backward(false /* forward */, startVertex, finalVertex, graph); + auto const useTwoThreads = graph.IsTwoThreadsReady(); + LOG(LINFO, + (useTwoThreads ? "Bidirectional A* in two threads." : "Bidirectional A* in one thread.")); + std::optional mtx; + + BidirectionalStepContext forward(mtx, true /* forward */, startVertex, finalVertex, graph); + BidirectionalStepContext backward(mtx, false /* forward */, startVertex, finalVertex, graph); auto & forwardParents = forward.GetParents(); auto & backwardParents = backward.GetParents(); @@ -567,11 +643,13 @@ AStarAlgorithm::FindPathBidirectional(P & params, auto bestPathReducedLength = kZeroDistance; auto bestPathRealLength = kZeroDistance; - forward.UpdateDistance(State(startVertex, kZeroDistance)); - forward.queue.push(State(startVertex, kZeroDistance, forward.ConsistentHeuristic(startVertex))); + forward.Init(startVertex); + backward.Init(finalVertex); - backward.UpdateDistance(State(finalVertex, kZeroDistance)); - backward.queue.push(State(finalVertex, kZeroDistance, backward.ConsistentHeuristic(finalVertex))); + if (useTwoThreads) + FindPathBidirectionalTwoThreadStep(epsilon, params, forward, backward); + + CHECK(!mtx.has_value(), ("Mutex should be destroyed.")); // To use the search code both for backward and forward directions // we keep the pointers to everything related to the search in the @@ -581,7 +659,7 @@ AStarAlgorithm::FindPathBidirectional(P & params, BidirectionalStepContext * nxt = &backward; auto const getResult = [&]() { - if (!params.m_checkLengthCallback(bestPathRealLength)) + if (!params.m_checkLengthCallback(bestPathRealLength, true /* forward */)) return Result::NoPath; ReconstructPathBidirectional(cur->bestVertex, nxt->bestVertex, cur->parent, nxt->parent, @@ -594,15 +672,14 @@ AStarAlgorithm::FindPathBidirectional(P & params, return Result::OK; }; - std::vector adj; - // It is not necessary to check emptiness for both queues here // because if we have not found a path by the time one of the // queues is exhausted, we never will. uint32_t steps = 0; PeriodicPollCancellable periodicCancellable(params.m_cancellable); - while (!cur->queue.empty() && !nxt->queue.empty()) + // One thread step. + while (cur->IsNextVertex() && nxt->IsNextVertex()) { ++steps; @@ -627,23 +704,27 @@ AStarAlgorithm::FindPathBidirectional(P & params, // It would be a mistake to make a decision based on real path lengths because // several top states in a priority queue may have equal reduced path lengths and // different real path lengths. - if (curTop + nxtTop >= bestPathReducedLength - epsilon) return getResult(); } - State const stateV = cur->queue.top(); - cur->queue.pop(); + // In case of two thread version it's necessary to process last edges got on two thread step. + // The information is kept in |cur->stateV| and |cur->adj|. + // |cur->stateV| should be checked before call |cur->TopAndPopState()|. + auto const firstStepAfterTwoThreads = cur->stateV != std::nullopt; + + State const stateV = cur->TopAndPopState(); if (cur->ExistsStateWithBetterDistance(stateV)) continue; + if (!firstStepAfterTwoThreads) + { + params.m_onVisitedVertexCallback(stateV.vertex, cur->GetEndVertex(), true /* forward */, mtx); + cur->FillAdjacencyList(stateV); + } - params.m_onVisitedVertexCallback(stateV.vertex, - cur->forward ? cur->finalVertex : cur->startVertex); - - cur->GetAdjacencyList(stateV, adj); auto const & pV = stateV.heuristic; - for (auto const & edge : adj) + for (auto const & edge : cur->adj) { State stateW(edge.GetTarget(), kZeroDistance); @@ -660,15 +741,14 @@ AStarAlgorithm::FindPathBidirectional(P & params, stateW.distance = stateV.distance + std::max(reducedWeight, kZeroDistance); auto const fullLength = weight + stateV.distance + cur->pS - pV; - if (!params.m_checkLengthCallback(fullLength)) + if (!params.m_checkLengthCallback(fullLength, true /* forward */)) continue; if (cur->ExistsStateWithBetterDistance(stateW, epsilon)) continue; stateW.heuristic = pW; - cur->UpdateDistance(stateW); - cur->UpdateParent(stateW.vertex, stateV.vertex); + cur->Update(stateW, stateV); if (auto op = nxt->GetDistance(stateW.vertex); op) { @@ -677,8 +757,9 @@ AStarAlgorithm::FindPathBidirectional(P & params, // find the reduced length of the path's parts in the reduced forward and backward graphs. auto const curPathReducedLength = stateW.distance + distW; // No epsilon here: it is ok to overshoot slightly. - if ((!foundAnyPath || bestPathReducedLength > curPathReducedLength) && - graph.AreWavesConnectible(forwardParents, stateW.vertex, backwardParents)) + auto const connectible = graph.AreWavesConnectible(forwardParents, stateW.vertex, + backwardParents); + if ((!foundAnyPath || bestPathReducedLength > curPathReducedLength) && connectible) { bestPathReducedLength = curPathReducedLength; @@ -692,7 +773,7 @@ AStarAlgorithm::FindPathBidirectional(P & params, } } - if (stateW.vertex != (cur->forward ? cur->finalVertex : cur->startVertex)) + if (stateW.vertex != cur->GetEndVertex()) cur->queue.push(stateW); } } @@ -738,6 +819,7 @@ AStarAlgorithm::AdjustRoute(P & params, Context context(graph); PeriodicPollCancellable periodicCancellable(params.m_cancellable); + std::optional dummy; auto visitVertex = [&](Vertex const & vertex) { if (periodicCancellable.IsCancelled()) @@ -746,7 +828,7 @@ AStarAlgorithm::AdjustRoute(P & params, return false; } - params.m_onVisitedVertexCallback(startVertex, vertex); + params.m_onVisitedVertexCallback(startVertex, vertex, true /* forward */, dummy); auto it = remainingDistances.find(vertex); if (it != remainingDistances.cend()) @@ -767,7 +849,7 @@ AStarAlgorithm::AdjustRoute(P & params, }; auto const filterStates = [&](State const & state) { - return params.m_checkLengthCallback(state.distance); + return params.m_checkLengthCallback(state.distance, true /* forward */); }; auto const reducedToRealLength = [&](State const & state) { return state.distance; }; @@ -848,4 +930,111 @@ AStarAlgorithm::Context::ReconstructPath(Vertex const & v, { AStarAlgorithm::ReconstructPath(v, m_parents, path); } + +template +template +void AStarAlgorithm::FindPathBidirectionalTwoThreadStep( + Weight const & epsilon, P & params, + AStarAlgorithm::BidirectionalStepContext & forward, + AStarAlgorithm::BidirectionalStepContext & backward) const +{ + CHECK(&*forward.mtx == &*backward.mtx, + ("forward and backward waves have to contain the same mutex.")); + auto & mtx = forward.mtx; + mtx.emplace(); + auto wave = [&epsilon, ¶ms](BidirectionalStepContext & context, + BidirectionalStepContext & oppositeContext, + std::atomic & exit) { + LOG(LINFO, ("---FindPath-------wave---------------------------", + context.forward ? "forward" : "backward", "queue size:", context.queue.size(), "exit:", exit.load())); + size_t i = 0; + while (!context.queue.empty() && !exit.load()) + { + // Note. In case of exception in copy c-tor |context.queue| structure is not damaged + // because the routing is stopped. + State const stateV = context.queue.top(); + context.queue.pop(); + + if (context.ExistsStateWithBetterDistance(stateV)) + continue; + + params.m_onVisitedVertexCallback(stateV.vertex, context.GetEndVertex(), context.forward, context.mtx); + + context.FillAdjacencyList(stateV); + auto const & pV = stateV.heuristic; + + // To get outgoing or ingoing edges |context.FillAdjacencyList(stateV)| is called above. + // Then some of got edges is places to the queue, but one edge should be places only once. + // |toQueue| is needed to guaranty this in the monument of switching to one thread step. + // It means that if forward and backward waves intersects on some edge, two thread + // step should be stopped and this edge and edges after it according to the queue + // should be processed with one thread step below. + std::vector toQueue; + toQueue.reserve(context.adj.size()); + bool intersectsWithOtherWave = false; + + for (auto const & edge : context.adj) + { + State stateW(edge.GetTarget(), kZeroDistance); + + if (stateV.vertex == stateW.vertex) + continue; + + auto const weight = edge.GetWeight(); + auto const pW = context.ConsistentHeuristic(stateW.vertex); + auto const reducedWeight = weight + pW - pV; + + CHECK_GREATER_OR_EQUAL( + reducedWeight, -epsilon, + ("Invariant violated for:", "v =", stateV.vertex, "w =", stateW.vertex)); + + stateW.distance = stateV.distance + std::max(reducedWeight, kZeroDistance); + + auto const fullLength = weight + stateV.distance + context.pS - pV; + if (!params.m_checkLengthCallback(fullLength, context.forward)) + continue; + + if (context.ExistsStateWithBetterDistance(stateW, epsilon)) + continue; + + stateW.heuristic = pW; + ++i; + if (intersectsWithOtherWave || oppositeContext.GetDistance(stateW.vertex)) + intersectsWithOtherWave = true; + + toQueue.push_back(stateW); + } + if (intersectsWithOtherWave) + { + // |context.stateV| is set here and |context.adj| is already set. + context.stateV = stateV; + exit.store(true); + LOG(LINFO, ("---FindPath-------wave end---------------------------", + context.forward ? "forward" : "backward", i)); + return; + } + else + { + for (auto const & stateW : toQueue) + context.UpdateAndPushIfNotEnd(stateW, stateV); + } + } + + LOG(LINFO, ("----FindPath------wave end---------------------------", + context.forward ? "forward" : "backward", + context.queue.empty() ? "queue is empty" : (exit.load() ? "Another wave is finished" : "STRANGE BEHAVIOR"), i)); + exit.store(true); + }; + + std::atomic shouldExit = false; + PeriodicPollCancellable periodicCancellable(params.m_cancellable); + + // Starting two wave in two threads. + threads::SimpleThread backwardWave(wave, std::ref(backward), std::ref(forward), std::ref(shouldExit)); + wave(forward, backward, shouldExit); + backwardWave.join(); + + LOG(LINFO, ("-------Two thread part of FindPath-------Finished-----------------")); + mtx.reset(); +} } // namespace routing diff --git a/routing/base/astar_graph.hpp b/routing/base/astar_graph.hpp index 37e95e34665..b4df7f66251 100644 --- a/routing/base/astar_graph.hpp +++ b/routing/base/astar_graph.hpp @@ -20,7 +20,10 @@ class AStarGraph using Parents = ska::bytell_hash_map; - virtual Weight HeuristicCostEstimate(Vertex const & from, Vertex const & to) = 0; + constexpr AStarGraph() {} + virtual ~AStarGraph() = default; + + virtual Weight HeuristicCostEstimate(Vertex const & from, Vertex const & to, bool isOutgoing) = 0; virtual void GetOutgoingEdgesList(astar::VertexData const & vertexData, std::vector & edges) = 0; @@ -34,7 +37,7 @@ class AStarGraph virtual Weight GetAStarWeightEpsilon(); - virtual ~AStarGraph() = default; + virtual bool IsTwoThreadsReady() const { return false; } }; template diff --git a/routing/cross_mwm_index_graph.hpp b/routing/cross_mwm_index_graph.hpp index a613076d9b2..746b0686111 100644 --- a/routing/cross_mwm_index_graph.hpp +++ b/routing/cross_mwm_index_graph.hpp @@ -276,10 +276,12 @@ class CrossMwmIndexGraph final ReaderSourceFile src(reader); auto it = m_connectors.find(numMwmId); if (it == m_connectors.end()) + { it = m_connectors .emplace(numMwmId, CrossMwmConnector( numMwmId, connector::GetFeaturesOffset())) .first; + } fn(m_vehicleType, it->second, src); return it->second; diff --git a/routing/fake_ending.cpp b/routing/fake_ending.cpp index de96339a702..07eaf637af4 100644 --- a/routing/fake_ending.cpp +++ b/routing/fake_ending.cpp @@ -61,9 +61,9 @@ FakeEnding MakeFakeEnding(vector const & segments, m2::PointD const & p { auto const & segment = segments[i]; - bool const oneWay = graph.IsOneWay(segment.GetMwmId(), segment.GetFeatureId()); - auto const & frontJunction = graph.GetJunction(segment, true /* front */); - auto const & backJunction = graph.GetJunction(segment, false /* front */); + bool const oneWay = graph.IsOneWay(segment.GetMwmId(), segment.GetFeatureId(), true /* isOutgoing */); + auto const & frontJunction = graph.GetJunction(segment, true /* front */, true /* isOutgoing */); + auto const & backJunction = graph.GetJunction(segment, false /* front */, true /* isOutgoing */); auto const & projectedJunction = CalcProjectionToSegment(backJunction, frontJunction, point); ending.m_projections.emplace_back(segment, oneWay, frontJunction, backJunction, @@ -79,7 +79,7 @@ FakeEnding MakeFakeEnding(vector const & segments, m2::PointD const & p FakeEnding MakeFakeEnding(Segment const & segment, m2::PointD const & point, IndexGraph & graph) { - auto const & road = graph.GetGeometry().GetRoad(segment.GetFeatureId()); + auto const & road = graph.GetGeometry().GetRoad(segment.GetFeatureId(), true /* isOutgoing */); bool const oneWay = road.IsOneWay(); auto const & frontJunction = road.GetJunction(segment.GetPointId(true /* front */)); auto const & backJunction = road.GetJunction(segment.GetPointId(false /* front */)); diff --git a/routing/features_road_graph.cpp b/routing/features_road_graph.cpp index dbe363343e2..b2313d33075 100644 --- a/routing/features_road_graph.cpp +++ b/routing/features_road_graph.cpp @@ -41,7 +41,11 @@ FeaturesRoadGraph::Value::Value(DataSource const & dataSource, MwmSet::MwmHandle if (!m_mwmHandle.IsAlive()) return; - m_altitudeLoader = make_unique(dataSource, m_mwmHandle.GetId()); + // Note. FeaturesRoadGraph is always used now from one thread. But if to find the best + // edge for start and finish from two threads the instance of FeaturesRoadGraph + // should have two thread support. + m_altitudeLoader = make_unique(dataSource, m_mwmHandle.GetId(), + false /* twoThreadsReady */); } FeaturesRoadGraph::CrossCountryVehicleModel::CrossCountryVehicleModel( @@ -311,7 +315,8 @@ void FeaturesRoadGraph::ExtractRoadInfo(FeatureID const & featureId, FeatureType geometry::Altitudes altitudes; if (value.m_altitudeLoader) { - altitudes = value.m_altitudeLoader->GetAltitudes(featureId.m_index, ft.GetPointsCount()); + altitudes = value.m_altitudeLoader->GetAltitudes(featureId.m_index, ft.GetPointsCount(), + true /* isOutgoing */); } else { diff --git a/routing/geometry.cpp b/routing/geometry.cpp index 98aa30e2ff0..6debcb4f5c2 100644 --- a/routing/geometry.cpp +++ b/routing/geometry.cpp @@ -24,8 +24,10 @@ using namespace std; namespace { // @TODO(bykoianko) Consider setting cache size based on available memory. -// Maximum road geometry cache size in items. +// Maximum road geometry cache size in items in case twoThreadsReady == false. size_t constexpr kRoadsCacheSize = 5000; +// Maximum road geometry cache size in items in case twoThreadsReady == true. +size_t constexpr kRoadsCacheSizeTwoCaches = 3500; double CalcFerryDurationHours(string const & durationHours, double roadLenKm) { @@ -60,16 +62,19 @@ class GeometryLoaderImpl final : public GeometryLoader { public: GeometryLoaderImpl(DataSource const & dataSource, MwmSet::MwmHandle const & handle, - shared_ptr vehicleModel, - AttrLoader attrLoader, bool loadAltitudes); + shared_ptr vehicleModel, AttrLoader attrLoader, + bool loadAltitudes, bool twoThreadsReady); // GeometryLoader overrides: - void Load(uint32_t featureId, RoadGeometry & road) override; + void Load(uint32_t featureId, RoadGeometry & road, bool isOutgoing) override; private: + bool IsTwoThreadsReady() const { return m_guardBwd != nullptr; } + shared_ptr m_vehicleModel; AttrLoader m_attrLoader; FeaturesLoaderGuard m_guard; + unique_ptr m_guardBwd; string const m_country; feature::AltitudeLoader m_altitudeLoader; bool const m_loadAltitudes; @@ -78,12 +83,15 @@ class GeometryLoaderImpl final : public GeometryLoader GeometryLoaderImpl::GeometryLoaderImpl(DataSource const & dataSource, MwmSet::MwmHandle const & handle, shared_ptr vehicleModel, - AttrLoader attrLoader, bool loadAltitudes) + AttrLoader attrLoader, bool loadAltitudes, + bool twoThreadsReady) : m_vehicleModel(move(vehicleModel)) , m_attrLoader(move(attrLoader)) , m_guard(dataSource, handle.GetId()) + , m_guardBwd(twoThreadsReady ? make_unique(dataSource, handle.GetId()) + : nullptr) , m_country(handle.GetInfo()->GetCountryName()) - , m_altitudeLoader(dataSource, handle.GetId()) + , m_altitudeLoader(dataSource, handle.GetId(), twoThreadsReady) , m_loadAltitudes(loadAltitudes) { CHECK(handle.IsAlive(), ()); @@ -92,21 +100,31 @@ GeometryLoaderImpl::GeometryLoaderImpl(DataSource const & dataSource, CHECK(m_attrLoader.m_maxspeeds, ()); } -void GeometryLoaderImpl::Load(uint32_t featureId, RoadGeometry & road) +void GeometryLoaderImpl::Load(uint32_t featureId, RoadGeometry & road, bool isOutgoing) { - auto feature = m_guard.GetFeatureByIndex(featureId); + unique_ptr feature; + if (IsTwoThreadsReady()) + { + feature = isOutgoing ? m_guard.GetFeatureByIndex(featureId) + : m_guardBwd->GetFeatureByIndex(featureId); + } + else + { + feature = m_guard.GetFeatureByIndex(featureId); + } if (!feature) MYTHROW(RoutingException, ("Feature", featureId, "not found in ", m_country)); feature->ParseGeometry(FeatureType::BEST_GEOMETRY); geometry::Altitudes const * altitudes = nullptr; + if (m_loadAltitudes) - altitudes = &(m_altitudeLoader.GetAltitudes(featureId, feature->GetPointsCount())); + altitudes = &(m_altitudeLoader.GetAltitudes(featureId, feature->GetPointsCount(), isOutgoing)); road.Load(*m_vehicleModel, *feature, altitudes, m_attrLoader.m_cityRoads->IsCityRoad(featureId), m_attrLoader.m_maxspeeds->GetMaxspeed(featureId)); - m_altitudeLoader.ClearCache(); + m_altitudeLoader.ClearCache(isOutgoing); } // FileGeometryLoader ------------------------------------------------------------------------------ @@ -116,7 +134,7 @@ class FileGeometryLoader final : public GeometryLoader FileGeometryLoader(string const & fileName, shared_ptr vehicleModel); // GeometryLoader overrides: - void Load(uint32_t featureId, RoadGeometry & road) override; + void Load(uint32_t featureId, RoadGeometry & road, bool isOutgoing) override; private: FeaturesVectorTest m_featuresVector; @@ -149,8 +167,9 @@ FileGeometryLoader::FileGeometryLoader(string const & fileName, CHECK(m_vehicleModel, ()); } -void FileGeometryLoader::Load(uint32_t featureId, RoadGeometry & road) +void FileGeometryLoader::Load(uint32_t featureId, RoadGeometry & road, bool isOutgoing) { + CHECK(isOutgoing, ("FileGeometryLoader() is not ready for two threads feature parsing.")); auto feature = m_featuresVector.GetVector().GetByIndex(featureId); CHECK(feature, ()); feature->ParseGeometry(FeatureType::BEST_GEOMETRY); @@ -253,33 +272,47 @@ double RoadGeometry::GetRoadLengthM() const } // Geometry ---------------------------------------------------------------------------------------- -Geometry::Geometry(unique_ptr loader) - : m_loader(move(loader)) - , m_featureIdToRoad(make_unique( - kRoadsCacheSize, - [this](uint32_t featureId, RoadGeometry & road) { m_loader->Load(featureId, road); })) +Geometry::Geometry(std::unique_ptr loader, bool twoThreadsReady) + : m_loader(move(loader)) + , m_featureIdToRoad(MakeCache(twoThreadsReady ? kRoadsCacheSizeTwoCaches : kRoadsCacheSize, + true /* isOutgoing */)) + , m_featureIdToRoadBwd( + twoThreadsReady ? MakeCache(kRoadsCacheSizeTwoCaches, false /* isOutgoing */) : nullptr) { CHECK(m_loader, ()); } -RoadGeometry const & Geometry::GetRoad(uint32_t featureId) +RoadGeometry const & Geometry::GetRoad(uint32_t featureId, bool isOutgoing) { ASSERT(m_featureIdToRoad, ()); ASSERT(m_loader, ()); + if (IsTwoThreadsReady()) + { + return isOutgoing ? m_featureIdToRoad->GetValue(featureId) + : m_featureIdToRoadBwd->GetValue(featureId); + } return m_featureIdToRoad->GetValue(featureId); } +std::unique_ptr Geometry::MakeCache(size_t cacheSize, + bool isOutgoing) const +{ + return make_unique(cacheSize, + [this, isOutgoing](uint32_t featureId, RoadGeometry & road) { + m_loader->Load(featureId, road, isOutgoing); + }); +} + // static -unique_ptr GeometryLoader::Create(DataSource const & dataSource, - MwmSet::MwmHandle const & handle, - shared_ptr vehicleModel, - AttrLoader && attrLoader, - bool loadAltitudes) +unique_ptr GeometryLoader::Create( + DataSource const & dataSource, MwmSet::MwmHandle const & handle, + std::shared_ptr vehicleModel, AttrLoader && attrLoader, + bool loadAltitudes, bool twoThreadsReady) { CHECK(handle.IsAlive(), ()); return make_unique(dataSource, handle, vehicleModel, move(attrLoader), - loadAltitudes); + loadAltitudes, twoThreadsReady); } // static diff --git a/routing/geometry.hpp b/routing/geometry.hpp index 1d60cc4f3c4..370cbf87084 100644 --- a/routing/geometry.hpp +++ b/routing/geometry.hpp @@ -108,14 +108,14 @@ class GeometryLoader public: virtual ~GeometryLoader() = default; - virtual void Load(uint32_t featureId, RoadGeometry & road) = 0; + virtual void Load(uint32_t featureId, RoadGeometry & road, bool isOutgoing) = 0; // handle should be alive: it is caller responsibility to check it. static std::unique_ptr Create(DataSource const & dataSource, MwmSet::MwmHandle const & handle, std::shared_ptr vehicleModel, - AttrLoader && attrLoader, - bool loadAltitudes); + AttrLoader && attrLoader, bool loadAltitudes, + bool twoThreadsReady); /// This is for stand-alone work. /// Use in generator_tool and unit tests. @@ -135,24 +135,29 @@ class Geometry final { public: Geometry() = default; - explicit Geometry(std::unique_ptr loader); + explicit Geometry(std::unique_ptr loader, bool twoThreadsReady = false); /// \note The reference returned by the method is valid until the next call of GetRoad() /// of GetPoint() methods. - RoadGeometry const & GetRoad(uint32_t featureId); + RoadGeometry const & GetRoad(uint32_t featureId, bool isOutgoing); /// \note The reference returned by the method is valid until the next call of GetRoad() /// of GetPoint() methods. - ms::LatLon const & GetPoint(RoadPoint const & rp) + ms::LatLon const & GetPoint(RoadPoint const & rp, bool isOutgoing) { - return GetRoad(rp.GetFeatureId()).GetPoint(rp.GetPointId()); + return GetRoad(rp.GetFeatureId(), isOutgoing).GetPoint(rp.GetPointId()); } private: using RoutingFifoCache = FifoCache>; + bool IsTwoThreadsReady() const { return m_featureIdToRoadBwd != nullptr; } + std::unique_ptr MakeCache(size_t cacheSize, bool isOutgoing) const; + std::unique_ptr m_loader; std::unique_ptr m_featureIdToRoad; + // Geometry cache |m_featureIdToRoadBwd| is used from the thread of backward wave of A*. + std::unique_ptr m_featureIdToRoadBwd; }; } // namespace routing diff --git a/routing/guides_connections.hpp b/routing/guides_connections.hpp index f5598a86c2e..3e613ad6636 100644 --- a/routing/guides_connections.hpp +++ b/routing/guides_connections.hpp @@ -107,6 +107,11 @@ class GuidesConnections // Returns fake ending associated with checkpoint by its index |checkpointIdx|. FakeEnding GetFakeEnding(size_t checkpointIdx) const; + void Clear() + { + m_checkpointsFakeEndings.clear(); + } + private: // Fills |ConnectionToOsm| for checkpoints for its further attachment to the roads graph. void AddConnectionToOsm(size_t checkpointIdx, Segment const & real, diff --git a/routing/index_graph.cpp b/routing/index_graph.cpp index d168dbda3a8..aa1371c5c27 100644 --- a/routing/index_graph.cpp +++ b/routing/index_graph.cpp @@ -48,7 +48,7 @@ bool IndexGraph::IsJoint(RoadPoint const & roadPoint) const return m_roadIndex.GetJointId(roadPoint) != Joint::kInvalidId; } -bool IndexGraph::IsJointOrEnd(Segment const & segment, bool fromStart) +bool IndexGraph::IsJointOrEnd(Segment const & segment, bool fromStart, bool isOutgoing) { if (IsJoint(segment.GetRoadPoint(fromStart))) return true; @@ -59,7 +59,7 @@ bool IndexGraph::IsJointOrEnd(Segment const & segment, bool fromStart) if (pointId == 0) return true; - uint32_t const pointsNumber = GetGeometry().GetRoad(segment.GetFeatureId()).GetPointsCount(); + uint32_t const pointsNumber = GetGeometry().GetRoad(segment.GetFeatureId(), isOutgoing).GetPointsCount(); return pointId + 1 == pointsNumber; } @@ -112,7 +112,7 @@ void IndexGraph::GetLastPointsForJoint(vector const & children, for (auto const & child : children) { uint32_t const startPointId = child.GetPointId(!isOutgoing /* front */); - uint32_t const pointsNumber = m_geometry->GetRoad(child.GetFeatureId()).GetPointsCount(); + uint32_t const pointsNumber = m_geometry->GetRoad(child.GetFeatureId(), isOutgoing).GetPointsCount(); CHECK_LESS(startPointId, pointsNumber, ()); uint32_t endPointId; @@ -251,7 +251,7 @@ void IndexGraph::GetNeighboringEdges(astar::VertexData con vector & edges, Parents const & parents, bool useAccessConditional) { - RoadGeometry const & road = m_geometry->GetRoad(rp.GetFeatureId()); + RoadGeometry const & road = m_geometry->GetRoad(rp.GetFeatureId(), isOutgoing); if (!road.IsValid()) return; @@ -280,7 +280,7 @@ void IndexGraph::GetNeighboringEdges(astar::VertexData con void IndexGraph::GetSegmentCandidateForRoadPoint(RoadPoint const & rp, NumMwmId numMwmId, bool isOutgoing, std::vector & children) { - RoadGeometry const & road = m_geometry->GetRoad(rp.GetFeatureId()); + RoadGeometry const & road = m_geometry->GetRoad(rp.GetFeatureId(), isOutgoing); if (!road.IsValid()) return; @@ -319,6 +319,9 @@ void IndexGraph::GetSegmentCandidateForJoint(Segment const & parent, bool isOutg /// \param |parentWeights| - see |IndexGraphStarterJoints::GetEdgeList| method about this argument. /// Shortly - in case of |isOutgoing| == false, method saves here the weights /// from parent to firstChildren. +/// \note Despite the fact the method is not constant it still may be called from two +/// threads. One should call it with |isOutgoing| == true and another one with +/// |isOutgoing| == false. void IndexGraph::ReconstructJointSegment(astar::VertexData const & parentVertexData, Segment const & parent, vector const & firstChildren, @@ -399,8 +402,8 @@ void IndexGraph::ReconstructJointSegment(astar::VertexData cons return; auto const weight = - CalculateEdgeWeight(EdgeEstimator::Purpose::Weight, isOutgoing, from, to, weightToFrom); + CalculateEdgeWeight(EdgeEstimator::Purpose::Weight, from, to, isOutgoing, weightToFrom); edges.emplace_back(to, weight); } -IndexGraph::PenaltyData IndexGraph::GetRoadPenaltyData(Segment const & segment) +IndexGraph::PenaltyData IndexGraph::GetRoadPenaltyData(Segment const & segment, bool isOutgoing) { - auto const & road = m_geometry->GetRoad(segment.GetFeatureId()); + auto const & road = m_geometry->GetRoad(segment.GetFeatureId(), isOutgoing); PenaltyData result(road.IsPassThroughAllowed(), road.GetRoutingOptions().Has(RoutingOptions::Road::Ferry)); @@ -457,10 +460,11 @@ IndexGraph::PenaltyData IndexGraph::GetRoadPenaltyData(Segment const & segment) } RouteWeight IndexGraph::GetPenalties(EdgeEstimator::Purpose purpose, Segment const & u, - Segment const & v, optional const & prevWeight) + Segment const & v, bool isOutgoing, + optional const & prevWeight) { - auto const & fromPenaltyData = GetRoadPenaltyData(u); - auto const & toPenaltyData = GetRoadPenaltyData(v); + auto const & fromPenaltyData = GetRoadPenaltyData(u, isOutgoing); + auto const & toPenaltyData = GetRoadPenaltyData(v, isOutgoing); // Route crosses border of pass-through/non-pass-through area if |u| and |v| have different // pass through restrictions. int8_t const passThroughPenalty = @@ -536,7 +540,7 @@ bool IndexGraph::IsUTurnAndRestricted(Segment const & parent, Segment const & ch uint32_t const featureId = parent.GetFeatureId(); uint32_t const turnPoint = parent.GetPointId(isOutgoing); - auto const & roadGeometry = m_geometry->GetRoad(featureId); + auto const & roadGeometry = m_geometry->GetRoad(featureId, isOutgoing); RoadPoint const rp = parent.GetRoadPoint(isOutgoing); if (m_roadIndex.GetJointId(rp) == Joint::kInvalidId && !roadGeometry.IsEndPointId(turnPoint)) @@ -555,16 +559,16 @@ bool IndexGraph::IsUTurnAndRestricted(Segment const & parent, Segment const & ch return uTurn.m_atTheEnd && turnPoint == n - 1; } -RouteWeight IndexGraph::CalculateEdgeWeight(EdgeEstimator::Purpose purpose, bool isOutgoing, - Segment const & from, Segment const & to, +RouteWeight IndexGraph::CalculateEdgeWeight(EdgeEstimator::Purpose purpose, Segment const & from, + Segment const & to, bool isOutgoing, std::optional const & prevWeight) { auto const & segment = isOutgoing ? to : from; - auto const & road = m_geometry->GetRoad(segment.GetFeatureId()); + auto const & road = m_geometry->GetRoad(segment.GetFeatureId(), isOutgoing); auto const weight = RouteWeight(m_estimator->CalcSegmentWeight(segment, road, purpose)); auto const & penalties = - GetPenalties(purpose, isOutgoing ? from : to, isOutgoing ? to : from, prevWeight); + GetPenalties(purpose, isOutgoing ? from : to, isOutgoing ? to : from, isOutgoing, prevWeight); return weight + penalties; } diff --git a/routing/index_graph.hpp b/routing/index_graph.hpp index 9c45e69cde5..f32664ce628 100644 --- a/routing/index_graph.hpp +++ b/routing/index_graph.hpp @@ -108,14 +108,16 @@ class IndexGraph final } bool IsJoint(RoadPoint const & roadPoint) const; - bool IsJointOrEnd(Segment const & segment, bool fromStart); + bool IsJointOrEnd(Segment const & segment, bool fromStart, bool isOutgoing); void GetLastPointsForJoint(std::vector const & children, bool isOutgoing, std::vector & lastPoints); WorldGraphMode GetMode() const; - ms::LatLon const & GetPoint(Segment const & segment, bool front) + ms::LatLon const & GetPoint(Segment const & segment, bool front, bool isOutgoing) { - return GetGeometry().GetRoad(segment.GetFeatureId()).GetPoint(segment.GetPointId(front)); + return GetGeometry() + .GetRoad(segment.GetFeatureId(), isOutgoing) + .GetPoint(segment.GetPointId(front)); } /// \brief Check, that we can go to |currentFeatureId|. @@ -129,8 +131,11 @@ class IndexGraph final bool IsUTurnAndRestricted(Segment const & parent, Segment const & child, bool isOutgoing) const; - RouteWeight CalculateEdgeWeight(EdgeEstimator::Purpose purpose, bool isOutgoing, - Segment const & from, Segment const & to, + /// \note Despite the fact the method is not constant it still may be called from two + /// threads. One should call it with |isOutgoing| == true and another one with + /// |isOutgoing| == false. + RouteWeight CalculateEdgeWeight(EdgeEstimator::Purpose purpose, + Segment const & from, Segment const & to, bool isOutgoing, std::optional const & prevWeight = std::nullopt); template @@ -164,14 +169,14 @@ class IndexGraph final bool m_isFerry; }; - PenaltyData GetRoadPenaltyData(Segment const & segment); + PenaltyData GetRoadPenaltyData(Segment const & segment, bool isOutgoing); /// \brief Calculates penalties for moving from |u| to |v|. /// \param |prevWeight| uses for fetching access:conditional. In fact it is time when user /// will be at |u|. This time is based on start time of route building and weight of calculated /// path until |u|. RouteWeight GetPenalties(EdgeEstimator::Purpose purpose, Segment const & u, Segment const & v, - std::optional const & prevWeight); + bool isOutgoing, std::optional const & prevWeight); void GetSegmentCandidateForRoadPoint(RoadPoint const & rp, NumMwmId numMwmId, bool isOutgoing, std::vector & children); diff --git a/routing/index_graph_loader.cpp b/routing/index_graph_loader.cpp index 95adc4d4d6a..4ecfc370742 100644 --- a/routing/index_graph_loader.cpp +++ b/routing/index_graph_loader.cpp @@ -16,10 +16,13 @@ #include "coding/files_container.hpp" #include "base/assert.hpp" +#include "base/optional_lock_guard.hpp" #include "base/timer.hpp" #include #include +#include +#include #include #include @@ -31,16 +34,26 @@ using namespace std; class IndexGraphLoaderImpl final : public IndexGraphLoader { public: - IndexGraphLoaderImpl(VehicleType vehicleType, bool loadAltitudes, shared_ptr numMwmIds, + IndexGraphLoaderImpl(VehicleType vehicleType, bool loadAltitudes, bool twoThreadsReady, + shared_ptr numMwmIds, shared_ptr vehicleModelFactory, shared_ptr estimator, DataSource & dataSource, RoutingOptions routingOptions = RoutingOptions()); + /// |GetGeometry()| and |GetIndexGraph()| return a references to items in container |m_graphs|. + /// They may be called from different threads in case of two thread bidirectional A*. + /// The references they return are not constant but it's ok. The code should works with them + /// taking into account |isOutgoing| parameter. On the other hand these methods may add items to + /// |m_graphs| under a mutex. So it's possible that while one thread is working with a reference + /// returned by |GetGeometry()| or |GetIndexGraph()| the other thread is adding item to |m_graphs| + /// and the hash table is rehashing. Everything should work correctly because according + /// to the standard rehashing of std::unordered_map keeps references. // IndexGraphLoader overrides: Geometry & GetGeometry(NumMwmId numMwmId) override; IndexGraph & GetIndexGraph(NumMwmId numMwmId) override; vector GetSpeedCameraInfo(Segment const & segment) override; void Clear() override; + bool IsTwoThreadsReady() const override; private: struct GraphAttrs @@ -59,6 +72,7 @@ class IndexGraphLoaderImpl final : public IndexGraphLoader shared_ptr m_vehicleModelFactory; shared_ptr m_estimator; + optional m_graphsMtx; unordered_map m_graphs; unordered_map>> m_cachedCameras; @@ -71,7 +85,7 @@ class IndexGraphLoaderImpl final : public IndexGraphLoader }; IndexGraphLoaderImpl::IndexGraphLoaderImpl( - VehicleType vehicleType, bool loadAltitudes, shared_ptr numMwmIds, + VehicleType vehicleType, bool loadAltitudes, bool twoThreadsReady, shared_ptr numMwmIds, shared_ptr vehicleModelFactory, shared_ptr estimator, DataSource & dataSource, RoutingOptions routingOptions) @@ -81,6 +95,7 @@ IndexGraphLoaderImpl::IndexGraphLoaderImpl( , m_numMwmIds(move(numMwmIds)) , m_vehicleModelFactory(move(vehicleModelFactory)) , m_estimator(move(estimator)) + , m_graphsMtx(twoThreadsReady ? std::make_optional() : std::nullopt) , m_avoidRoutingOptions(routingOptions) { CHECK(m_numMwmIds, ()); @@ -90,6 +105,7 @@ IndexGraphLoaderImpl::IndexGraphLoaderImpl( Geometry & IndexGraphLoaderImpl::GetGeometry(NumMwmId numMwmId) { + base::OptionalLockGuard guard(m_graphsMtx); auto it = m_graphs.find(numMwmId); if (it != m_graphs.end()) return *it->second.m_geometry; @@ -99,6 +115,7 @@ Geometry & IndexGraphLoaderImpl::GetGeometry(NumMwmId numMwmId) IndexGraph & IndexGraphLoaderImpl::GetIndexGraph(NumMwmId numMwmId) { + base::OptionalLockGuard guard(m_graphsMtx); auto it = m_graphs.find(numMwmId); if (it != m_graphs.end()) { @@ -106,6 +123,8 @@ IndexGraph & IndexGraphLoaderImpl::GetIndexGraph(NumMwmId numMwmId) : *CreateIndexGraph(numMwmId, it->second).m_indexGraph; } + // Note. For the reason of putting CreateIndexGraph() under the |guard| please see + // a detailed comment in ConvertRestrictionsOnlyUTurnToNo() method. return *CreateIndexGraph(numMwmId, CreateGeometry(numMwmId)).m_indexGraph; } @@ -195,8 +214,9 @@ IndexGraphLoaderImpl::GraphAttrs & IndexGraphLoaderImpl::CreateGeometry(NumMwmId m_vehicleModelFactory->GetVehicleModelForCountry(file.GetName()); auto & graph = m_graphs[numMwmId]; - graph.m_geometry = make_shared(GeometryLoader::Create( - m_dataSource, handle, vehicleModel, AttrLoader(m_dataSource, handle), m_loadAltitudes)); + graph.m_geometry = make_shared( + GeometryLoader::Create(m_dataSource, handle, vehicleModel, AttrLoader(m_dataSource, handle), + m_loadAltitudes, IsTwoThreadsReady()), IsTwoThreadsReady()); return graph; } @@ -219,7 +239,13 @@ IndexGraphLoaderImpl::GraphAttrs & IndexGraphLoaderImpl::CreateIndexGraph( return graph; } -void IndexGraphLoaderImpl::Clear() { m_graphs.clear(); } +void IndexGraphLoaderImpl::Clear() +{ + base::OptionalLockGuard guard(m_graphsMtx); + m_graphs.clear(); +} + +bool IndexGraphLoaderImpl::IsTwoThreadsReady() const { return m_graphsMtx.has_value(); } bool ReadRoadAccessFromMwm(MwmValue const & mwmValue, VehicleType vehicleType, RoadAccess & roadAccess) @@ -248,13 +274,14 @@ namespace routing { // static unique_ptr IndexGraphLoader::Create( - VehicleType vehicleType, bool loadAltitudes, shared_ptr numMwmIds, + VehicleType vehicleType, bool loadAltitudes, bool twoThreadsReady, shared_ptr numMwmIds, shared_ptr vehicleModelFactory, shared_ptr estimator, DataSource & dataSource, RoutingOptions routingOptions) { - return make_unique(vehicleType, loadAltitudes, numMwmIds, vehicleModelFactory, - estimator, dataSource, routingOptions); + return make_unique(vehicleType, loadAltitudes, twoThreadsReady, numMwmIds, + vehicleModelFactory, estimator, dataSource, + routingOptions); } void DeserializeIndexGraph(MwmValue const & mwmValue, VehicleType vehicleType, IndexGraph & graph) diff --git a/routing/index_graph_loader.hpp b/routing/index_graph_loader.hpp index 110f24c6612..a68f5ffad2f 100644 --- a/routing/index_graph_loader.hpp +++ b/routing/index_graph_loader.hpp @@ -27,9 +27,11 @@ class IndexGraphLoader // Because several cameras can lie on one segment we return vector of them. virtual std::vector GetSpeedCameraInfo(Segment const & segment) = 0; virtual void Clear() = 0; + virtual bool IsTwoThreadsReady() const = 0; static std::unique_ptr Create( - VehicleType vehicleType, bool loadAltitudes, std::shared_ptr numMwmIds, + VehicleType vehicleType, bool loadAltitudes, bool twoThreadsReady, + std::shared_ptr numMwmIds, std::shared_ptr vehicleModelFactory, std::shared_ptr estimator, DataSource & dataSource, RoutingOptions routingOptions = RoutingOptions()); diff --git a/routing/index_graph_starter.cpp b/routing/index_graph_starter.cpp index ccc13413d57..1482b8a3ae0 100644 --- a/routing/index_graph_starter.cpp +++ b/routing/index_graph_starter.cpp @@ -71,8 +71,8 @@ IndexGraphStarter::IndexGraphStarter(FakeEnding const & startEnding, m_otherEndings.push_back(startEnding); m_otherEndings.push_back(finishEnding); - auto const startPoint = GetPoint(GetStartSegment(), false /* front */); - auto const finishPoint = GetPoint(GetFinishSegment(), true /* front */); + auto const startPoint = GetPoint(GetStartSegment(), false /* front */, true /* isOutgoing */); + auto const finishPoint = GetPoint(GetFinishSegment(), true /* front */, true /* isOutgoing */); m_startToFinishDistanceM = ms::DistanceOnEarth(startPoint, finishPoint); } @@ -83,8 +83,8 @@ void IndexGraphStarter::Append(FakeEdgesContainer const & container) // It's important to calculate distance after m_fake.Append() because // we don't have finish segment in fake graph before m_fake.Append(). - auto const startPoint = GetPoint(GetStartSegment(), false /* front */); - auto const finishPoint = GetPoint(GetFinishSegment(), true /* front */); + auto const startPoint = GetPoint(GetStartSegment(), false /* front */, true /* isOutgoing */); + auto const finishPoint = GetPoint(GetFinishSegment(), true /* front */, true /* isOutgoing */); m_startToFinishDistanceM = ms::DistanceOnEarth(startPoint, finishPoint); m_fakeNumerationStart += container.m_fake.GetSize(); } @@ -111,50 +111,51 @@ bool IndexGraphStarter::ConvertToReal(Segment & segment) const return m_fake.FindReal(Segment(segment), segment); } -LatLonWithAltitude const & IndexGraphStarter::GetJunction(Segment const & segment, bool front) const +LatLonWithAltitude const & IndexGraphStarter::GetJunction(Segment const & segment, bool front, + bool isOutgoing) const { if (IsGuidesSegment(segment)) return m_guides.GetJunction(segment, front); if (!IsFakeSegment(segment)) - return m_graph.GetJunction(segment, front); + return m_graph.GetJunction(segment, front, isOutgoing); auto const & vertex = m_fake.GetVertex(segment); return front ? vertex.GetJunctionTo() : vertex.GetJunctionFrom(); } LatLonWithAltitude const & IndexGraphStarter::GetRouteJunction( - vector const & segments, size_t pointIndex) const + vector const & segments, size_t pointIndex, bool isOutgoing) const { CHECK(!segments.empty(), ()); CHECK_LESS_OR_EQUAL( pointIndex, segments.size(), ("Point with index", pointIndex, "does not exist in route with size", segments.size())); if (pointIndex == segments.size()) - return GetJunction(segments[pointIndex - 1], true /* front */); - return GetJunction(segments[pointIndex], false); + return GetJunction(segments[pointIndex - 1], true /* front */, isOutgoing); + return GetJunction(segments[pointIndex], false /* front */, isOutgoing); } -ms::LatLon const & IndexGraphStarter::GetPoint(Segment const & segment, bool front) const +ms::LatLon const & IndexGraphStarter::GetPoint(Segment const & segment, bool front, bool isOutgoing) const { - return GetJunction(segment, front).GetLatLon(); + return GetJunction(segment, front, isOutgoing).GetLatLon(); } -bool IndexGraphStarter::IsRoutingOptionsGood(Segment const & segment) const +bool IndexGraphStarter::IsRoutingOptionsGood(Segment const & segment, bool isOutgoing) const { - return m_graph.IsRoutingOptionsGood(segment); + return m_graph.IsRoutingOptionsGood(segment, isOutgoing); } -RoutingOptions IndexGraphStarter::GetRoutingOptions(Segment const & segment) const +RoutingOptions IndexGraphStarter::GetRoutingOptions(Segment const & segment, bool isOutgoing) const { if (segment.IsRealSegment()) - return m_graph.GetRoutingOptions(segment); + return m_graph.GetRoutingOptions(segment, isOutgoing); Segment real; if (!m_fake.FindReal(segment, real)) return {}; - return m_graph.GetRoutingOptions(real); + return m_graph.GetRoutingOptions(real, isOutgoing); } set IndexGraphStarter::GetMwms() const @@ -179,12 +180,12 @@ set IndexGraphStarter::GetFinishMwms() const return mwms; } -bool IndexGraphStarter::CheckLength(RouteWeight const & weight) +bool IndexGraphStarter::CheckLength(RouteWeight const & weight, bool isOutgoing) { // We allow 1 pass-through/non-pass-through zone changes per ending located in // non-pass-through zone to allow user to leave this zone. int8_t const numPassThroughChangesAllowed = - (StartPassThroughAllowed() ? 0 : 1) + (FinishPassThroughAllowed() ? 0 : 1); + (StartPassThroughAllowed(isOutgoing) ? 0 : 1) + (FinishPassThroughAllowed(isOutgoing) ? 0 : 1); return weight.GetNumPassThroughChanges() <= numPassThroughChangesAllowed && m_graph.CheckLength(weight, m_startToFinishDistanceM); @@ -201,15 +202,17 @@ void IndexGraphStarter::GetEdgesList(astar::VertexData const & v // Weight used only when isOutgoing = false for passing to m_guides and placing to |edges|. RouteWeight ingoingSegmentWeight; if (!isOutgoing) - ingoingSegmentWeight = CalcSegmentWeight(segment, EdgeEstimator::Purpose::Weight); + ingoingSegmentWeight = CalcSegmentWeight(segment, isOutgoing, EdgeEstimator::Purpose::Weight); if (IsFakeSegment(segment)) { Segment real; if (m_fake.FindReal(segment, real)) { - bool const haveSameFront = GetJunction(segment, true /* front */) == GetJunction(real, true); - bool const haveSameBack = GetJunction(segment, false /* front */) == GetJunction(real, false); + bool const haveSameFront = GetJunction(segment, true /* front */, isOutgoing) == + GetJunction(real, true /* front */, isOutgoing); + bool const haveSameBack = GetJunction(segment, false /* front */, isOutgoing) == + GetJunction(real, false /* front */, isOutgoing); if ((isOutgoing && haveSameFront) || (!isOutgoing && haveSameBack)) { if (IsGuidesSegment(real)) @@ -227,8 +230,9 @@ void IndexGraphStarter::GetEdgesList(astar::VertexData const & v for (auto const & s : m_fake.GetEdges(segment, isOutgoing)) { - edges.emplace_back(s, isOutgoing ? CalcSegmentWeight(s, EdgeEstimator::Purpose::Weight) - : ingoingSegmentWeight); + edges.emplace_back(s, isOutgoing + ? CalcSegmentWeight(s, isOutgoing, EdgeEstimator::Purpose::Weight) + : ingoingSegmentWeight); } } else if (IsGuidesSegment(segment)) @@ -254,22 +258,22 @@ RouteWeight IndexGraphStarter::CalcGuidesSegmentWeight(Segment const & segment, return m_graph.CalcOffroadWeight(from.GetLatLon(), to.GetLatLon(), purpose); } -RouteWeight IndexGraphStarter::CalcSegmentWeight(Segment const & segment, +RouteWeight IndexGraphStarter::CalcSegmentWeight(Segment const & segment, bool isOutgoing, EdgeEstimator::Purpose purpose) const { if (IsGuidesSegment(segment)) return CalcGuidesSegmentWeight(segment, purpose); if (!IsFakeSegment(segment)) - return m_graph.CalcSegmentWeight(segment, purpose); + return m_graph.CalcSegmentWeight(segment, isOutgoing, purpose); auto const & vertex = m_fake.GetVertex(segment); Segment real; if (m_fake.FindReal(segment, real)) { auto const partLen = ms::DistanceOnEarth(vertex.GetPointFrom(), vertex.GetPointTo()); - auto const fullLen = - ms::DistanceOnEarth(GetPoint(real, false /* front */), GetPoint(real, true /* front */)); + auto const fullLen = ms::DistanceOnEarth(GetPoint(real, false /* front */, isOutgoing), + GetPoint(real, true /* front */, isOutgoing)); // Note 1. |fullLen| == 0.0 in case of Segment(s) with the same ends. // Note 2. There is the following logic behind |return 0.0 * m_graph.CalcSegmentWeight(real, ...);|: // it's necessary to return a instance of the structure |RouteWeight| with zero wight. @@ -277,7 +281,7 @@ RouteWeight IndexGraphStarter::CalcSegmentWeight(Segment const & segment, // may be kept in it and it is up to |RouteWeight| to know how to multiply by zero. Weight const weight = IsGuidesSegment(real) ? CalcGuidesSegmentWeight(real, purpose) - : m_graph.CalcSegmentWeight(real, purpose); + : m_graph.CalcSegmentWeight(real, isOutgoing, purpose); if (fullLen == 0.0) return 0.0 * weight; @@ -287,14 +291,14 @@ RouteWeight IndexGraphStarter::CalcSegmentWeight(Segment const & segment, return m_graph.CalcOffroadWeight(vertex.GetPointFrom(), vertex.GetPointTo(), purpose); } -double IndexGraphStarter::CalculateETA(Segment const & from, Segment const & to) const +double IndexGraphStarter::CalculateETA(Segment const & from, Segment const & to, bool isOutgoing) const { // We don't distinguish fake segment weight and fake segment transit time. if (IsFakeSegment(to)) - return CalcSegmentWeight(to, EdgeEstimator::Purpose::ETA).GetWeight(); + return CalcSegmentWeight(to, isOutgoing, EdgeEstimator::Purpose::ETA).GetWeight(); if (IsFakeSegment(from)) - return CalculateETAWithoutPenalty(to); + return CalculateETAWithoutPenalty(to, isOutgoing); if (IsGuidesSegment(from) || IsGuidesSegment(to)) { @@ -302,28 +306,28 @@ double IndexGraphStarter::CalculateETA(Segment const & from, Segment const & to) if (IsGuidesSegment(from)) res += CalcGuidesSegmentWeight(from, EdgeEstimator::Purpose::ETA).GetWeight(); else - res += CalculateETAWithoutPenalty(from); + res += CalculateETAWithoutPenalty(from, isOutgoing); if (IsGuidesSegment(to)) res += CalcGuidesSegmentWeight(to, EdgeEstimator::Purpose::ETA).GetWeight(); else - res += CalculateETAWithoutPenalty(to); + res += CalculateETAWithoutPenalty(to, isOutgoing); return res; } - return m_graph.CalculateETA(from, to); + return m_graph.CalculateETA(from, to, isOutgoing); } -double IndexGraphStarter::CalculateETAWithoutPenalty(Segment const & segment) const +double IndexGraphStarter::CalculateETAWithoutPenalty(Segment const & segment, bool isOutgoing) const { if (IsFakeSegment(segment)) - return CalcSegmentWeight(segment, EdgeEstimator::Purpose::ETA).GetWeight(); + return CalcSegmentWeight(segment, isOutgoing, EdgeEstimator::Purpose::ETA).GetWeight(); if (IsGuidesSegment(segment)) return CalcGuidesSegmentWeight(segment, EdgeEstimator::Purpose::ETA).GetWeight(); - return m_graph.CalculateETAWithoutPenalty(segment); + return m_graph.CalculateETAWithoutPenalty(segment, isOutgoing); } void IndexGraphStarter::AddEnding(FakeEnding const & thisEnding) @@ -528,43 +532,44 @@ void IndexGraphStarter::AddFakeEdges(Segment const & segment, bool isOutgoing, v { // |segment| |s| // *------------>*-----------> - bool const sIsOutgoing = - GetJunction(segment, true /* front */) == GetJunction(s, false /* front */); + bool const sIsOutgoing = GetJunction(segment, true /* front */, isOutgoing) == + GetJunction(s, false /* front */, isOutgoing); // |s| |segment| // *------------>*-----------> - bool const sIsIngoing = - GetJunction(s, true /* front */) == GetJunction(segment, false /* front */); + bool const sIsIngoing = GetJunction(s, true /* front */, isOutgoing) == + GetJunction(segment, false /* front */, isOutgoing); if ((isOutgoing && sIsOutgoing) || (!isOutgoing && sIsIngoing)) { // For ingoing edges we use source weight which is the same for |s| and for |edge| and is // already calculated. - fakeEdges.emplace_back(s, isOutgoing ? CalcSegmentWeight(s, EdgeEstimator::Purpose::Weight) - : edge.GetWeight()); + fakeEdges.emplace_back( + s, isOutgoing ? CalcSegmentWeight(s, isOutgoing, EdgeEstimator::Purpose::Weight) + : edge.GetWeight()); } } } edges.insert(edges.end(), fakeEdges.begin(), fakeEdges.end()); } -bool IndexGraphStarter::EndingPassThroughAllowed(Ending const & ending) +bool IndexGraphStarter::EndingPassThroughAllowed(Ending const & ending, bool isOutgoing) { - return any_of(ending.m_real.cbegin(), ending.m_real.cend(), [this](Segment const & s) { + return any_of(ending.m_real.cbegin(), ending.m_real.cend(), [this, isOutgoing](Segment const & s) { if (IsGuidesSegment(s)) return true; - return m_graph.IsPassThroughAllowed(s.GetMwmId(), s.GetFeatureId()); + return m_graph.IsPassThroughAllowed(s.GetMwmId(), s.GetFeatureId(), isOutgoing); }); } -bool IndexGraphStarter::StartPassThroughAllowed() +bool IndexGraphStarter::StartPassThroughAllowed(bool isOutgoing) { - return EndingPassThroughAllowed(m_start); + return EndingPassThroughAllowed(m_start, isOutgoing); } -bool IndexGraphStarter::FinishPassThroughAllowed() +bool IndexGraphStarter::FinishPassThroughAllowed(bool isOutgoing) { - return EndingPassThroughAllowed(m_finish); + return EndingPassThroughAllowed(m_finish, isOutgoing); } Segment IndexGraphStarter::GetFakeSegmentAndIncr() diff --git a/routing/index_graph_starter.hpp b/routing/index_graph_starter.hpp index 1b476a76b66..5f6ec1612db 100644 --- a/routing/index_graph_starter.hpp +++ b/routing/index_graph_starter.hpp @@ -73,13 +73,13 @@ class IndexGraphStarter : public AStarGraph const & route, - size_t pointIndex) const; - ms::LatLon const & GetPoint(Segment const & segment, bool front) const; + LatLonWithAltitude const & GetJunction(Segment const & segment, bool front, bool isOutgoing) const; + LatLonWithAltitude const & GetRouteJunction(std::vector const & route, size_t pointIndex, + bool isOutgoing) const; + ms::LatLon const & GetPoint(Segment const & segment, bool front, bool isOutgoing) const; - bool IsRoutingOptionsGood(Segment const & segment) const; - RoutingOptions GetRoutingOptions(Segment const & segment) const; + bool IsRoutingOptionsGood(Segment const & segment, bool isOutgoing) const; + RoutingOptions GetRoutingOptions(Segment const & segment, bool isOutgoing) const; uint32_t GetNumFakeSegments() const { @@ -95,7 +95,10 @@ class IndexGraphStarter : public AStarGraph const & parentVertexData, Segment const & segment, bool isOutgoing, std::vector & edges, @@ -119,10 +122,10 @@ class IndexGraphStarter : public AStarGraph & parents) override @@ -142,6 +145,8 @@ class IndexGraphStarter : public AStarGraph & edges) const @@ -149,16 +154,17 @@ class IndexGraphStarter : public AStarGraph & edges) const; // Checks whether ending belongs to pass-through or non-pass-through zone. - bool EndingPassThroughAllowed(Ending const & ending); + bool EndingPassThroughAllowed(Ending const & ending, bool isOutgoing); // Start segment is located in a pass-through/non-pass-through area. - bool StartPassThroughAllowed(); + bool StartPassThroughAllowed(bool isOutgoing); // Finish segment is located in a pass-through/non-pass-through area. - bool FinishPassThroughAllowed(); + bool FinishPassThroughAllowed(bool isOutgoing); static uint32_t constexpr kFakeFeatureId = FakeFeatureIds::kIndexGraphStarterId; WorldGraph & m_graph; diff --git a/routing/index_graph_starter_joints.hpp b/routing/index_graph_starter_joints.hpp index 4b79080c100..3241ed85308 100644 --- a/routing/index_graph_starter_joints.hpp +++ b/routing/index_graph_starter_joints.hpp @@ -13,10 +13,12 @@ #include "geometry/latlon.hpp" #include "base/assert.hpp" +#include "base/optional_lock_guard.hpp" #include #include #include +#include #include #include #include @@ -30,7 +32,12 @@ template class IndexGraphStarterJoints : public AStarGraph { public: - explicit IndexGraphStarterJoints(Graph & graph) : m_graph(graph) {} + /// \note This class may be used in two modes. For one thread A* and two threads A*. + /// To create an instance of the class with synchronization staff |graph.IsTwoThreadsReady()| + /// should return true. It means the class is ready for calls HeuristicCostEstimate(), + /// GetOutgoingEdgesList() and GetIngoingEdgesList() from two different threads. + explicit IndexGraphStarterJoints(Graph & graph); + IndexGraphStarterJoints(Graph & graph, Segment const & startSegment, Segment const & endSegment); @@ -40,13 +47,13 @@ class IndexGraphStarterJoints : public AStarGraph const & vertexData, std::vector & edges) override @@ -93,6 +100,8 @@ class IndexGraphStarterJoints : public AStarGraph const & vertexData, @@ -163,7 +172,7 @@ class IndexGraphStarterJoints : public AStarGraph FindFirstJoints(Segment const & startSegment, bool fromStart); + std::vector FindFirstJoints(Segment const & startSegment, bool fromStart, bool isOutgoing); JointSegment CreateInvisibleJoint(Segment const & segment, bool start); @@ -184,6 +193,9 @@ class IndexGraphStarterJoints : public AStarGraph m_savedWeight; // JointSegment consists of two segments of one feature. @@ -209,8 +225,14 @@ class IndexGraphStarterJoints : public AStarGraph m_path; }; + // |m_reconstructedFakeJointsMtx| is used for synchronization access to |m_reconstructedFakeJoints| + // while bidirectional A* is calculation the route in two threads mode. + std::optional m_reconstructedFakeJointsMtx; std::map m_reconstructedFakeJoints; + // |m_startOutEdges| and |m_endOutEdges| are read from GetOutgoingEdgesList() and + // GetIngoingEdgesList() methods but they may be used from two threads concurrently because + // they are set only on initialization step and in Reset() method and then they are only read. // List of JointEdges that are outgoing from start. std::vector m_startOutEdges; // List of incoming to finish. @@ -221,18 +243,34 @@ class IndexGraphStarterJoints : public AStarGraph -IndexGraphStarterJoints::IndexGraphStarterJoints(Graph & graph, - Segment const & startSegment, +std::optional GetOptionalMutex(Graph const & graph) +{ + return graph.IsTwoThreadsReady() ? std::make_optional() : std::nullopt; +} + +template +IndexGraphStarterJoints::IndexGraphStarterJoints(Graph & graph) + : m_graph(graph) + , m_reconstructedFakeJointsMtx(GetOptionalMutex(graph)) +{ +} + +template +IndexGraphStarterJoints::IndexGraphStarterJoints(Graph & graph, Segment const & startSegment, Segment const & endSegment) - : m_graph(graph), m_startSegment(startSegment), m_endSegment(endSegment) + : m_graph(graph) + , m_startSegment(startSegment) + , m_endSegment(endSegment) + , m_reconstructedFakeJointsMtx(GetOptionalMutex(graph)) { Init(m_startSegment, m_endSegment); } template -IndexGraphStarterJoints::IndexGraphStarterJoints(Graph & graph, - Segment const & startSegment) - : m_graph(graph), m_startSegment(startSegment) +IndexGraphStarterJoints::IndexGraphStarterJoints(Graph & graph, Segment const & startSegment) + : m_graph(graph) + , m_startSegment(startSegment) + , m_reconstructedFakeJointsMtx(GetOptionalMutex(graph)) { InitEnding(startSegment, true /* start */); @@ -259,7 +297,7 @@ void IndexGraphStarterJoints::InitEnding(Segment const & ending, bool sta segment = ending; auto & point = start ? m_startPoint : m_endPoint; - point = m_graph.GetPoint(ending, true /* front */); + point = m_graph.GetPoint(ending, true /* front */, true /* isOutgoing */); auto & endingJoint = start ? m_startJoint : m_endJoint; if (IsRealSegment(ending)) @@ -272,10 +310,13 @@ void IndexGraphStarterJoints::InitEnding(Segment const & ending, bool sta endingJoint = CreateFakeJoint(loopSegment, loopSegment); } - m_reconstructedFakeJoints[endingJoint] = ReconstructedPath({ending}, start); + { + base::OptionalLockGuard guard(m_reconstructedFakeJointsMtx); + m_reconstructedFakeJoints[endingJoint] = ReconstructedPath({ending}, start); + } auto & outEdges = start ? m_startOutEdges : m_endOutEdges; - outEdges = FindFirstJoints(ending, start); + outEdges = FindFirstJoints(ending, start, true /* isOutgoing */); if (!start) { @@ -287,7 +328,8 @@ void IndexGraphStarterJoints::InitEnding(Segment const & ending, bool sta template RouteWeight IndexGraphStarterJoints::HeuristicCostEstimate(JointSegment const & from, - JointSegment const & to) + JointSegment const & to, + bool isOutgoing) { ASSERT(to == m_startJoint || to == m_endJoint, ("Invariant violated.")); bool toEnd = to == m_endJoint; @@ -295,6 +337,7 @@ RouteWeight IndexGraphStarterJoints::HeuristicCostEstimate(JointSegment c Segment fromSegment; if (from.IsFake() || IsInvisible(from)) { + base::OptionalLockGuard guard(m_reconstructedFakeJointsMtx); ASSERT_NOT_EQUAL(m_reconstructedFakeJoints.count(from), 0, ()); fromSegment = m_reconstructedFakeJoints[from].m_path.back(); } @@ -303,18 +346,18 @@ RouteWeight IndexGraphStarterJoints::HeuristicCostEstimate(JointSegment c fromSegment = from.GetSegment(false /* start */); } - return toEnd ? m_graph.HeuristicCostEstimate(fromSegment, m_endPoint) - : m_graph.HeuristicCostEstimate(fromSegment, m_startPoint); + return toEnd ? m_graph.HeuristicCostEstimate(fromSegment, m_endPoint, isOutgoing) + : m_graph.HeuristicCostEstimate(fromSegment, m_startPoint, isOutgoing); } template ms::LatLon const & -IndexGraphStarterJoints::GetPoint(JointSegment const & jointSegment, bool start) +IndexGraphStarterJoints::GetPoint(JointSegment const & jointSegment, bool start, bool isOutgoing) { Segment segment = jointSegment.IsFake() ? m_fakeJointSegments[jointSegment].GetSegment(start) : jointSegment.GetSegment(start); - return m_graph.GetPoint(segment, jointSegment.IsForward()); + return m_graph.GetPoint(segment, jointSegment.IsForward(), isOutgoing); } template @@ -328,11 +371,15 @@ std::vector IndexGraphStarterJoints::ReconstructJoint(JointSegme // In case of a fake vertex we return its prebuilt path. if (joint.IsFake()) { - auto const it = m_reconstructedFakeJoints.find(joint); - CHECK(it != m_reconstructedFakeJoints.cend(), ("Can not find such fake joint")); + std::vector path; + { + base::OptionalLockGuard guard(m_reconstructedFakeJointsMtx); + auto const it = m_reconstructedFakeJoints.find(joint); + CHECK(it != m_reconstructedFakeJoints.cend(), ("Can not find such fake joint")); - auto path = it->second.m_path; - ASSERT(!path.empty(), ()); + path = it->second.m_path; + ASSERT(!path.empty(), ()); + } if (path.front() == m_startSegment && path.back() == m_endSegment) path.pop_back(); @@ -568,7 +615,7 @@ JointSegment IndexGraphStarterJoints::CreateFakeJoint(Segment const & fro template std::vector IndexGraphStarterJoints::FindFirstJoints(Segment const & startSegment, - bool fromStart) + bool fromStart, bool isOutgoing) { Segment const & endSegment = fromStart ? m_endSegment : m_startSegment; @@ -609,6 +656,7 @@ std::vector IndexGraphStarterJoints::FindFirstJoints(Segment c result.emplace_back(fakeJoint, weight[beforeConvert]); std::vector path = reconstructPath(beforeConvert, fromStart); + base::OptionalLockGuard guard(m_reconstructedFakeJointsMtx); m_reconstructedFakeJoints.emplace(fakeJoint, ReconstructedPath(std::move(path), fromStart)); }; @@ -617,11 +665,11 @@ std::vector IndexGraphStarterJoints::FindFirstJoints(Segment c { CHECK(!IsRealSegment(fake), ()); - bool const hasSameFront = - m_graph.GetPoint(fake, true /* front */) == m_graph.GetPoint(segment, true); + bool const hasSameFront = m_graph.GetPoint(fake, true /* front */, true /* isOutgoing */) == + m_graph.GetPoint(segment, true /* front */, true /* isOutgoing */); - bool const hasSameBack = - m_graph.GetPoint(fake, false /* front */) == m_graph.GetPoint(segment, false); + bool const hasSameBack = m_graph.GetPoint(fake, false /* front */, true /* isOutgoing */) == + m_graph.GetPoint(segment, false /* front */, true /* isOutgoing */); return (fromStart && hasSameFront) || (!fromStart && hasSameBack); }; @@ -635,7 +683,7 @@ std::vector IndexGraphStarterJoints::FindFirstJoints(Segment c // or it's the real one and its end (RoadPoint) is |Joint|. if (((!IsRealSegment(segment) && m_graph.ConvertToReal(segment) && isEndOfSegment(beforeConvert, segment, fromStart)) || IsRealSegment(beforeConvert)) && - IsJoint(segment, fromStart)) + IsJoint(segment, fromStart, isOutgoing)) { addFake(segment, beforeConvert); continue; diff --git a/routing/index_road_graph.cpp b/routing/index_road_graph.cpp index 27350e908d7..8d088641e0a 100644 --- a/routing/index_road_graph.cpp +++ b/routing/index_road_graph.cpp @@ -97,9 +97,11 @@ void IndexRoadGraph::GetRouteEdges(EdgeVector & edges) const for (Segment const & segment : m_segments) { auto const & junctionFrom = - m_starter.GetJunction(segment, false /* front */).ToPointWithAltitude(); + m_starter.GetJunction(segment, false /* front */, true /* isOutgoing */) + .ToPointWithAltitude(); auto const & junctionTo = - m_starter.GetJunction(segment, true /* front */).ToPointWithAltitude(); + m_starter.GetJunction(segment, true /* front */, true /* isOutgoing */) + .ToPointWithAltitude(); if (IndexGraphStarter::IsFakeSegment(segment) || TransitGraph::IsTransitSegment(segment)) { @@ -159,8 +161,8 @@ void IndexRoadGraph::GetEdges(geometry::PointWithAltitude const & junction, bool edges.push_back(Edge::MakeReal( FeatureID(mwmId, segment.GetFeatureId()), segment.IsForward(), segment.GetSegmentIdx(), - m_starter.GetJunction(segment, false /* front */).ToPointWithAltitude(), - m_starter.GetJunction(segment, true /* front */).ToPointWithAltitude())); + m_starter.GetJunction(segment, false /* front */, isOutgoing).ToPointWithAltitude(), + m_starter.GetJunction(segment, true /* front */, isOutgoing).ToPointWithAltitude())); } } diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 11fc901e587..450e050ce62 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -305,7 +305,7 @@ IndexRouter::IndexRouter(VehicleType vehicleType, bool loadAltitudes, unique_ptr IndexRouter::MakeSingleMwmWorldGraph() { - auto worldGraph = MakeWorldGraph(); + auto worldGraph = MakeWorldGraph(false /* twoThreadsReady */); worldGraph->SetMode(WorldGraphMode::SingleMwm); return worldGraph; } @@ -367,7 +367,7 @@ bool IndexRouter::FindClosestProjectionToRoad(m2::PointD const & point, void IndexRouter::SetGuides(GuidesTracks && guides) { m_guides = GuidesConnections(guides); } RouterResultCode IndexRouter::CalculateRoute(Checkpoints const & checkpoints, - m2::PointD const & startDirection, + m2::PointD const & startDirection, bool useTwoThreads, bool adjustToPrevRoute, RouterDelegate const & delegate, Route & route) { @@ -407,7 +407,7 @@ RouterResultCode IndexRouter::CalculateRoute(Checkpoints const & checkpoints, } } - return DoCalculateRoute(checkpoints, startDirection, delegate, route); + return DoCalculateRoute(checkpoints, startDirection, delegate, useTwoThreads, route); } catch (RootException const & e) { @@ -552,7 +552,8 @@ void IndexRouter::AddGuidesOsmConnectionsToGraphStarter(size_t checkpointIdxFrom RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, - RouterDelegate const & delegate, Route & route) + RouterDelegate const & delegate, + bool useTwoThreads, Route & route) { m_lastRoute.reset(); // MwmId used for guides segments in RedressRoute(). @@ -585,7 +586,7 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, return RouterResultCode::NeedMoreMaps; TrafficStash::Guard guard(m_trafficStash); - unique_ptr graph = MakeWorldGraph(); + unique_ptr graph = MakeWorldGraph(useTwoThreads); vector segments; @@ -630,7 +631,7 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, // Stop building route if |finishCheckpoint| is not connected to OSM and is not connected to // the guides graph. if (!FindBestSegments(finishCheckpoint, m2::PointD::Zero() /* direction */, - false /* isOutgoing */, *graph, finishSegments, + true /* isOutgoing */, *graph, finishSegments, dummy /* bestSegmentIsAlmostCodirectional */) && finishFakeEnding.m_projections.empty()) { @@ -718,7 +719,7 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, m_lastRoute = make_unique(checkpoints.GetStart(), checkpoints.GetFinish(), route.GetSubroutes()); for (Segment const & segment : segments) - m_lastRoute->AddStep(segment, mercator::FromLatLon(starter->GetPoint(segment, true /* front */))); + m_lastRoute->AddStep(segment, mercator::FromLatLon(starter->GetPoint(segment, true /* front */, true /* isOutgoing */))); m_lastFakeEdges = make_unique(move(*starter)); @@ -800,8 +801,8 @@ RouterResultCode IndexRouter::CalculateSubrouteJointsMode( AStarLengthChecker(starter)); RoutingResult routingResult; - RouterResultCode const result = - FindPath(params, {} /* mwmIds */, routingResult, WorldGraphMode::Joints); + RouterResultCode const result = FindPath(params, + {} /* mwmIds */, routingResult, WorldGraphMode::Joints); if (result != RouterResultCode::NoError) return result; @@ -828,7 +829,8 @@ RouterResultCode IndexRouter::CalculateSubrouteNoLeapsMode( RoutingResult routingResult; set const mwmIds = starter.GetMwms(); RouterResultCode const result = - FindPath(params, mwmIds, routingResult, WorldGraphMode::NoLeaps); + FindPath(params, mwmIds, routingResult, + WorldGraphMode::NoLeaps); if (result != RouterResultCode::NoError) return result; @@ -863,7 +865,8 @@ RouterResultCode IndexRouter::CalculateSubrouteLeapsOnlyMode( RoutingResult routingResult; RouterResultCode const result = - FindPath(params, {} /* mwmIds */, routingResult, WorldGraphMode::LeapsOnly); + FindPath(params, {} /* mwmIds */, + routingResult, WorldGraphMode::LeapsOnly); progress->PushAndDropLastSubProgress(); @@ -890,7 +893,7 @@ RouterResultCode IndexRouter::AdjustRoute(Checkpoints const & checkpoints, { base::Timer timer; TrafficStash::Guard guard(m_trafficStash); - auto graph = MakeWorldGraph(); + auto graph = MakeWorldGraph(false /* twoThreadsReady */); graph->SetMode(WorldGraphMode::NoLeaps); vector startSegments; @@ -922,7 +925,7 @@ RouterResultCode IndexRouter::AdjustRoute(Checkpoints const & checkpoints, { auto const & step = steps[i]; prevEdges.emplace_back(step.GetSegment(), starter.CalcSegmentWeight(step.GetSegment(), - EdgeEstimator::Purpose::Weight)); + true /* isOutgoing */, EdgeEstimator::Purpose::Weight)); } using Visitor = JunctionVisitor; @@ -981,7 +984,7 @@ RouterResultCode IndexRouter::AdjustRoute(Checkpoints const & checkpoints, return RouterResultCode::NoError; } -unique_ptr IndexRouter::MakeWorldGraph() +unique_ptr IndexRouter::MakeWorldGraph(bool twoThreadsReady) { RoutingOptions routingOptions; if (m_vehicleType == VehicleType::Car) @@ -997,8 +1000,8 @@ unique_ptr IndexRouter::MakeWorldGraph() auto indexGraphLoader = IndexGraphLoader::Create( m_vehicleType == VehicleType::Transit ? VehicleType::Pedestrian : m_vehicleType, - m_loadAltitudes, m_numMwmIds, m_vehicleModelFactory, m_estimator, m_dataSource, - routingOptions); + m_loadAltitudes, twoThreadsReady, m_numMwmIds, m_vehicleModelFactory, + m_estimator, m_dataSource, routingOptions); if (m_vehicleType != VehicleType::Transit) { @@ -1331,8 +1334,8 @@ RouterResultCode IndexRouter::ProcessLeapsJoints(vector const & input, maxStart = max(maxStart, start); auto const contribCoef = static_cast(end - maxStart + 1) / (input.size()); - auto const startPoint = starter.GetPoint(input[start], true /* front */); - auto const endPoint = starter.GetPoint(input[end], true /* front */); + auto const startPoint = starter.GetPoint(input[start], true /* front */, true /* isOutgoing */); + auto const endPoint = starter.GetPoint(input[end], true /* front */, true /* isOutgoing */); progress->AppendSubProgress({startPoint, endPoint, contribCoef}); RouterResultCode resultCode = RouterResultCode::NoError; @@ -1379,8 +1382,8 @@ RouterResultCode IndexRouter::ProcessLeapsJoints(vector const & input, } LOG(LINFO, ("Can not find path", - "from:", starter.GetPoint(input[start], input[start].IsForward()), - "to:", starter.GetPoint(input[end], input[end].IsForward()))); + "from:", starter.GetPoint(input[start], input[start].IsForward(), true /* isOutgoing */), + "to:", starter.GetPoint(input[end], input[end].IsForward(), true /* isOutgoing */))); return false; }; @@ -1410,12 +1413,12 @@ RouterResultCode IndexRouter::ProcessLeapsJoints(vector const & input, if (!tryBuildRoute(prev, next, WorldGraphMode::JointSingleMwm, routingResult)) { - auto const prevPoint = starter.GetPoint(input[next], true); + auto const prevPoint = starter.GetPoint(input[next], true /* front */, true /* isOutgoing */); // |next + 1| - is the twin of |next| // |next + 2| - is the next exit. while (next + 2 < finishLeapStart && next != finishLeapStart) { - auto const point = starter.GetPoint(input[next + 2], true); + auto const point = starter.GetPoint(input[next + 2], true /* front */, true /* isOutgoing */); double const distBetweenExistsMeters = ms::DistanceOnEarth(point, prevPoint); static double constexpr kMinDistBetweenExitsM = 100000; // 100 km @@ -1474,7 +1477,7 @@ RouterResultCode IndexRouter::RedressRoute(vector const & segments, junctions.reserve(numPoints); for (size_t i = 0; i < numPoints; ++i) - junctions.emplace_back(starter.GetRouteJunction(segments, i).ToPointWithAltitude()); + junctions.emplace_back(starter.GetRouteJunction(segments, i, true /* isOutgoing */).ToPointWithAltitude()); IndexRoadGraph roadGraph(m_numMwmIds, starter, segments, junctions, m_dataSource); starter.GetGraph().SetMode(WorldGraphMode::NoLeaps); @@ -1486,12 +1489,12 @@ RouterResultCode IndexRouter::RedressRoute(vector const & segments, times.emplace_back(static_cast(0), 0.0); // Time at first route point - weight of first segment. - double time = starter.CalculateETAWithoutPenalty(segments.front()); + double time = starter.CalculateETAWithoutPenalty(segments.front(), true /* isOutgoing */); times.emplace_back(static_cast(1), time); for (size_t i = 1; i < segments.size(); ++i) { - time += starter.CalculateETA(segments[i - 1], segments[i]); + time += starter.CalculateETA(segments[i - 1], segments[i], true /* isOutgoing */); times.emplace_back(static_cast(i + 1), time); } @@ -1513,7 +1516,7 @@ RouterResultCode IndexRouter::RedressRoute(vector const & segments, // to use them. if (m_vehicleType == VehicleType::Car) { - routeSegment.SetRoadTypes(starter.GetRoutingOptions(segment)); + routeSegment.SetRoadTypes(starter.GetRoutingOptions(segment, true /* isOutgoing */)); if (segment.IsRealSegment() && !AreSpeedCamerasProhibited(m_numMwmIds->GetFile(segment.GetMwmId()))) { diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 84439914519..543b462ebb9 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -77,6 +77,11 @@ class IndexRouter : public IRouter traffic::TrafficCache const & trafficCache, DataSource & dataSource); std::unique_ptr MakeSingleMwmWorldGraph(); + /// @todo FindBestSegments() is called for start, finish and intermediate points of the route. + /// On a modern mobile device this method takes 200-300ms. + /// The most number of routes have no intermediate points. It's worth calling this method + /// for start on one thread and for finish and another one. It's not difficult to implement it + /// based on functionality which was developed for two-thread bidirectional A*. bool FindBestSegments(m2::PointD const & checkpoint, m2::PointD const & direction, bool isOutgoing, WorldGraph & worldGraph, std::vector & bestSegments); bool FindBestEdges(m2::PointD const & checkpoint, @@ -92,8 +97,9 @@ class IndexRouter : public IRouter void SetGuides(GuidesTracks && guides) override; RouterResultCode CalculateRoute(Checkpoints const & checkpoints, - m2::PointD const & startDirection, bool adjustToPrevRoute, - RouterDelegate const & delegate, Route & route) override; + m2::PointD const & startDirection, bool useTwoThreads, + bool adjustToPrevRoute, RouterDelegate const & delegate, + Route & route) override; bool FindClosestProjectionToRoad(m2::PointD const & point, m2::PointD const & direction, double radius, EdgeProj & proj) override; @@ -117,7 +123,8 @@ class IndexRouter : public IRouter RouterResultCode DoCalculateRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, - RouterDelegate const & delegate, Route & route); + RouterDelegate const & delegate, bool useTwoThreads, + Route & route); RouterResultCode CalculateSubroute(Checkpoints const & checkpoints, size_t subrouteIdx, RouterDelegate const & delegate, std::shared_ptr const & progress, @@ -128,7 +135,7 @@ class IndexRouter : public IRouter m2::PointD const & startDirection, RouterDelegate const & delegate, Route & route); - std::unique_ptr MakeWorldGraph(); + std::unique_ptr MakeWorldGraph(bool twoThreadsReady); /// \brief Removes all roads from |roads| which goes to dead ends and all road which /// is not good according to |worldGraph|. For car routing there are roads with hwtag nocar as well. @@ -207,13 +214,14 @@ class IndexRouter : public IRouter } template - RouterResultCode FindPath( - AStarParams & params, std::set const & mwmIds, - RoutingResult & routingResult, WorldGraphMode mode) const + RouterResultCode FindPath(AStarParams & params, std::set const & mwmIds, + RoutingResult & routingResult, + WorldGraphMode mode) const { AStarAlgorithm algorithm; return ConvertTransitResult( - mwmIds, ConvertResult(algorithm.FindPathBidirectional(params, routingResult))); + mwmIds, ConvertResult( + algorithm.FindPathBidirectional(params, routingResult))); } void SetupAlgorithmMode(IndexGraphStarter & starter, bool guidesActive = false) const; @@ -242,6 +250,8 @@ class IndexRouter : public IRouter std::shared_ptr m_numMwmIds; std::shared_ptr> m_numMwmTree; std::shared_ptr m_trafficStash; + // Note. |m_roadGraph| contains caches inside and is not ready for multithreading. + // So all calls of the methods of |m_roadGraph| should be synchronized. FeaturesRoadGraph m_roadGraph; std::shared_ptr m_estimator; diff --git a/routing/junction_visitor.hpp b/routing/junction_visitor.hpp index 9e09efcb443..b4d28419342 100644 --- a/routing/junction_visitor.hpp +++ b/routing/junction_visitor.hpp @@ -6,8 +6,12 @@ #include "geometry/point2d.hpp" +#include "base/optional_lock_guard.hpp" + #include #include +#include +#include namespace routing { @@ -27,20 +31,25 @@ class JunctionVisitor m_lastProgressPercent = progress->GetLastPercent(); } - void operator()(Vertex const & from, Vertex const & to) + void operator()(Vertex const & from, Vertex const & to, bool isOutgoing, + std::optional & m) { - ++m_visitCounter; - if (m_visitCounter % m_visitPeriod != 0) + if (!IncrementCounterAndCheckPeriod(isOutgoing ? m_visitCounter : m_visitCounterBwd)) return; - auto const & pointFrom = m_graph.GetPoint(from, true /* front */); + if (!m_graph.IsTwoThreadsReady()) + CHECK(!m.has_value(), ("Graph is not ready for two threads but there's a mutex.")); + + auto const & pointFrom = m_graph.GetPoint(from, true /* front */, isOutgoing); + auto const & pointTo = m_graph.GetPoint(to, true /* front */, isOutgoing); + + base::OptionalLockGuard guard(m); m_delegate.OnPointCheck(pointFrom); auto progress = m_progress.lock(); if (!progress) return; - auto const & pointTo = m_graph.GetPoint(to, true /* front */); auto const currentPercent = progress->UpdateProgress(pointFrom, pointTo); if (currentPercent - m_lastProgressPercent > kProgressInterval) { @@ -50,9 +59,19 @@ class JunctionVisitor } private: + bool IncrementCounterAndCheckPeriod(uint32_t & counter) + { + ++counter; + if (counter % m_visitPeriod == 0) + return true; + return false; + } + Graph & m_graph; RouterDelegate const & m_delegate; + // Only one counter is used depending on one or two thread version. uint32_t m_visitCounter = 0; + uint32_t m_visitCounterBwd = 0; uint32_t m_visitPeriod; std::weak_ptr m_progress; double m_lastProgressPercent = 0.0; diff --git a/routing/leaps_graph.cpp b/routing/leaps_graph.cpp index 6a295865f52..c94e1e724e9 100644 --- a/routing/leaps_graph.cpp +++ b/routing/leaps_graph.cpp @@ -10,8 +10,10 @@ namespace routing LeapsGraph::LeapsGraph(IndexGraphStarter & starter, MwmHierarchyHandler && hierarchyHandler) : m_starter(starter), m_hierarchyHandler(std::move(hierarchyHandler)) { - m_startPoint = m_starter.GetPoint(m_starter.GetStartSegment(), true /* front */); - m_finishPoint = m_starter.GetPoint(m_starter.GetFinishSegment(), true /* front */); + m_startPoint = + m_starter.GetPoint(m_starter.GetStartSegment(), true /* front */, true /* isOutgoing */); + m_finishPoint = + m_starter.GetPoint(m_starter.GetFinishSegment(), true /* front */, true /* isOutgoing */); m_startSegment = m_starter.GetStartSegment(); m_finishSegment = m_starter.GetFinishSegment(); } @@ -28,12 +30,13 @@ void LeapsGraph::GetIngoingEdgesList(astar::VertexData const & v GetEdgesList(vertexData.m_vertex, false /* isOutgoing */, edges); } -RouteWeight LeapsGraph::HeuristicCostEstimate(Segment const & from, Segment const & to) +RouteWeight LeapsGraph::HeuristicCostEstimate(Segment const & from, Segment const & to, + bool isOutgoing) { ASSERT(to == m_startSegment || to == m_finishSegment, ()); bool const toFinish = to == m_finishSegment; auto const & toPoint = toFinish ? m_finishPoint : m_startPoint; - return m_starter.HeuristicCostEstimate(from, toPoint); + return m_starter.HeuristicCostEstimate(from, toPoint, isOutgoing); } void LeapsGraph::GetEdgesList(Segment const & segment, bool isOutgoing, @@ -45,17 +48,17 @@ void LeapsGraph::GetEdgesList(Segment const & segment, bool isOutgoing, { CHECK(isOutgoing, ("Only forward wave of A* should get edges from start. Backward wave should " "stop when first time visit the |m_startSegment|.")); - return GetEdgesListFromStart(segment, edges); + return GetEdgesListFromStart(segment, isOutgoing, edges); } if (segment == m_finishSegment) { CHECK(!isOutgoing, ("Only backward wave of A* should get edges to finish. Forward wave should " "stop when first time visit the |m_finishSegment|.")); - return GetEdgesListToFinish(segment, edges); + return GetEdgesListToFinish(segment, isOutgoing, edges); } - if (!m_starter.IsRoutingOptionsGood(segment)) + if (!m_starter.IsRoutingOptionsGood(segment, isOutgoing)) return; auto & crossMwmGraph = m_starter.GetGraph().GetCrossMwmGraph(); @@ -78,7 +81,8 @@ void LeapsGraph::GetEdgesList(Segment const & segment, bool isOutgoing, crossMwmGraph.GetIngoingEdgeList(segment, edges); } -void LeapsGraph::GetEdgesListFromStart(Segment const & segment, std::vector & edges) +void LeapsGraph::GetEdgesListFromStart(Segment const & segment, bool isOutgoing, + std::vector & edges) { for (auto const mwmId : m_starter.GetStartEnding().m_mwmIds) { @@ -86,7 +90,7 @@ void LeapsGraph::GetEdgesListFromStart(Segment const & segment, std::vector & edges) +void LeapsGraph::GetEdgesListToFinish(Segment const & segment, bool isOutgoing, + std::vector & edges) { for (auto const mwmId : m_starter.GetFinishEnding().m_mwmIds) { @@ -102,7 +107,7 @@ void LeapsGraph::GetEdgesListToFinish(Segment const & segment, std::vector std::vector & edges) override; void GetIngoingEdgesList(astar::VertexData const & vertexData, std::vector & edges) override; - RouteWeight HeuristicCostEstimate(Segment const & from, Segment const & to) override; + RouteWeight HeuristicCostEstimate(Segment const & from, Segment const & to, + bool isOutgoing) override; RouteWeight GetAStarWeightEpsilon() override; // @} Segment const & GetStartSegment() const; Segment const & GetFinishSegment() const; - ms::LatLon const & GetPoint(Segment const & segment, bool front) const; + ms::LatLon const & GetPoint(Segment const & segment, bool front, bool isOutgoing) const; private: void GetEdgesList(Segment const & segment, bool isOutgoing, std::vector & edges); - void GetEdgesListFromStart(Segment const & segment, std::vector & edges); - void GetEdgesListToFinish(Segment const & segment, std::vector & edges); + void GetEdgesListFromStart(Segment const & segment, bool isOutgoing, + std::vector & edges); + void GetEdgesListToFinish(Segment const & segment, bool isOutgoing, std::vector & edges); ms::LatLon m_startPoint; ms::LatLon m_finishPoint; diff --git a/routing/leaps_postprocessor.cpp b/routing/leaps_postprocessor.cpp index 5426fafda87..06d186fa462 100644 --- a/routing/leaps_postprocessor.cpp +++ b/routing/leaps_postprocessor.cpp @@ -103,7 +103,8 @@ void LeapsPostProcessor::Init() for (size_t i = 1; i < m_path.size(); ++i) { auto const & segment = m_path[i]; - m_prefixSumETA[i] = m_prefixSumETA[i - 1] + m_starter.CalculateETAWithoutPenalty(segment); + m_prefixSumETA[i] = m_prefixSumETA[i - 1] + + m_starter.CalculateETAWithoutPenalty(segment, true /* isOutgoing */); CHECK_EQUAL(m_segmentToIndex.count(segment), 0, ()); m_segmentToIndex[segment] = i; @@ -146,7 +147,8 @@ auto LeapsPostProcessor::CalculateIntervalsToRelax() -> std::set segmentsData; auto const & segment = m_path[right]; - segmentsData.emplace(segment, SegmentData(0, m_starter.CalculateETAWithoutPenalty(segment))); + segmentsData.emplace(segment, SegmentData(0, m_starter.CalculateETAWithoutPenalty( + segment, true /* isOutgoing */))); FillIngoingPaths(segment, segmentsData); @@ -192,7 +194,8 @@ void LeapsPostProcessor::FillIngoingPaths( auto & current = segmentsData[state.m_vertex]; current.m_summaryETA = - parent.m_summaryETA + m_starter.CalculateETAWithoutPenalty(state.m_vertex); + parent.m_summaryETA + + m_starter.CalculateETAWithoutPenalty(state.m_vertex, true /* isOutgoing */); current.m_steps = parent.m_steps + 1; diff --git a/routing/restriction_loader.cpp b/routing/restriction_loader.cpp index 24d51a17be0..186a08194ef 100644 --- a/routing/restriction_loader.cpp +++ b/routing/restriction_loader.cpp @@ -162,7 +162,16 @@ void ConvertRestrictionsOnlyUTurnToNo(IndexGraph & graph, if (!graph.IsRoad(featureId)) continue; - uint32_t const n = graph.GetGeometry().GetRoad(featureId).GetPointsCount(); + // On the one hand to pass |isOutgoing| param to this method is quite difficult. To do that + // should be modified: AStarGraph::AreWavesConnectible() and methods in derived classes, + // two methods WorldGraph::AreWavesConnectible() and methods in derived classes and so on. + // About 110 lines should be modified. + // On the other hand loading ConvertRestrictionsOnlyUTurnToNo() is called only from + // RestrictionLoader constructor and this operation is planned to be implemented + // under mutex in case of two threaded bidirectional A star. + // To prevent inflating function signatures by adding |isOutgoing| parameter + // it should be hardcode as true here. + uint32_t const n = graph.GetGeometry().GetRoad(featureId, true /* isOutgoing */).GetPointsCount(); RoadJointIds const & joints = graph.GetRoad(uTurnRestriction.m_featureId); Joint::Id const joint = uTurnRestriction.m_viaIsFirstPoint ? joints.GetJointId(0) : joints.GetJointId(n - 1); diff --git a/routing/router.hpp b/routing/router.hpp index 73c76ebc5b6..3f03f95d81b 100644 --- a/routing/router.hpp +++ b/routing/router.hpp @@ -79,8 +79,9 @@ class IRouter /// @return ResultCode error code or NoError if route was initialised /// @see Cancellable virtual RouterResultCode CalculateRoute(Checkpoints const & checkpoints, - m2::PointD const & startDirection, bool adjust, - RouterDelegate const & delegate, Route & route) = 0; + m2::PointD const & startDirection, bool useTwoThreads, + bool adjust, RouterDelegate const & delegate, + Route & route) = 0; virtual bool FindClosestProjectionToRoad(m2::PointD const & point, m2::PointD const & direction, double radius, EdgeProj & proj) = 0; diff --git a/routing/routes_builder/routes_builder.cpp b/routing/routes_builder/routes_builder.cpp index 0e71515479b..36757d7ada9 100644 --- a/routing/routes_builder/routes_builder.cpp +++ b/routing/routes_builder/routes_builder.cpp @@ -302,7 +302,8 @@ RoutesBuilder::Processor::operator()(Params const & params) m_delegate->SetTimeout(params.m_timeoutSeconds); base::Timer timer; resultCode = m_router->CalculateRoute(params.m_checkpoints, m2::PointD::Zero(), - false /* adjustToPrevRoute */, *m_delegate, route); + false /* useTwoThreads */, false /* adjustToPrevRoute */, + *m_delegate, route); if (resultCode != RouterResultCode::NoError) break; diff --git a/routing/routing_benchmarks/helpers.cpp b/routing/routing_benchmarks/helpers.cpp index 6f8e8ceb34f..5a16bb829bb 100644 --- a/routing/routing_benchmarks/helpers.cpp +++ b/routing/routing_benchmarks/helpers.cpp @@ -154,9 +154,9 @@ void TestRouter(routing::IRouter & router, m2::PointD const & startPos, routing::RouterDelegate delegate; LOG(LINFO, ("Calculating routing ...", router.GetName())); base::Timer timer; - auto const resultCode = router.CalculateRoute(routing::Checkpoints(startPos, finalPos), - m2::PointD::Zero() /* startDirection */, - false /* adjust */, delegate, route); + auto const resultCode = router.CalculateRoute( + routing::Checkpoints(startPos, finalPos), m2::PointD::Zero() /* startDirection */, + false /* useTwoThreads */, false /* adjust */, delegate, route); double const elapsedSec = timer.ElapsedSeconds(); TEST_EQUAL(routing::RouterResultCode::NoError, resultCode, ()); TEST(route.IsValid(), ()); diff --git a/routing/routing_consistency_tests/routing_consistency_tests.cpp b/routing/routing_consistency_tests/routing_consistency_tests.cpp index 7870cd170fd..9245778dc8f 100644 --- a/routing/routing_consistency_tests/routing_consistency_tests.cpp +++ b/routing/routing_consistency_tests/routing_consistency_tests.cpp @@ -14,7 +14,9 @@ #include #include #include +#include #include +#include #include "3party/gflags/src/gflags/gflags.h" @@ -22,27 +24,50 @@ using namespace routing; using namespace std; using storage::CountryInfo; -double constexpr kMinimumRouteDistanceM = 10000.; +double constexpr kMinimumRouteDistanceM = 10000.0; double constexpr kRouteLengthAccuracy = 0.15; // Testing stub to make routing test tools linkable. static CommandLineOptions g_options; CommandLineOptions const & GetTestingOptions() {return g_options;} +// @TODO Fix comment +DEFINE_string(mode, "", "Routing consistency tests mode.\n" + "dist - builds route and compares with distance set in input_file. In that case " + "every line of input file should contain 5 numbers (doubles) separated by spaces: " + " \n" + "two_threads - builds route with the help of two threads bidirectional A* and " + "compares with one thread bidirectional A*. In that case " + "every line of input file should contain 4 numbers (doubles) separated by spaces: " + " "); DEFINE_string(input_file, "", "File with statistics output."); DEFINE_string(data_path, "../../data/", "Working directory, 'path_to_exe/../../data' if empty."); DEFINE_string(user_resource_path, "", "User defined resource path for classificator.txt and etc."); DEFINE_bool(verbose, false, "Output processed lines to log."); -DEFINE_uint64(confidence, 5, "Maximum test count for each single mwm file."); +DEFINE_uint64(confidence, 5, "Maximum test count for each single mwm file." + "Actual for dist mode only."); // Information about successful user routing. struct UserRoutingRecord { m2::PointD start; m2::PointD stop; - double distance; + double distanceM = 0.0; }; +double GetDouble(string const & incomingString, size_t & i) +{ + // Removes leading spaces. + while (i < incomingString.size() && incomingString[i] == ' ') + { + ++i; + }; + auto end = incomingString.find(" ", i); + string number = incomingString.substr(i, end - i); + i = end; + return stod(number); +} + // Parsing value from statistics. double GetDouble(string const & incomingString, string const & key) { @@ -51,9 +76,7 @@ double GetDouble(string const & incomingString, string const & key) return 0; // Skip "key=" it += key.size() + 1; - auto end = incomingString.find(" ", it); - string number = incomingString.substr(it, end - it); - return stod(number); + return GetDouble(incomingString, it); } // Decoding statistics line. Returns true if the incomeString is the valid record about @@ -71,12 +94,25 @@ bool ParseUserString(string const & incomeString, UserRoutingRecord & result) return false; // Extract numbers from a record. - result.distance = GetDouble(incomeString, "distance"); + result.distanceM = GetDouble(incomeString, "distance"); result.start = mercator::FromLatLon(GetDouble(incomeString, "startLat"), GetDouble(incomeString, "startLon")); result.stop = mercator::FromLatLon(GetDouble(incomeString, "finalLat"), GetDouble(incomeString, "finalLon")); return true; } +// Parsing a line with four numbers: . +void ParseRouteLine(string const & incomeString, UserRoutingRecord & result) +{ + size_t i = 0; + auto startLat = GetDouble(incomeString, i); + auto startLon = GetDouble(incomeString, i); + result.start = mercator::FromLatLon(startLat, startLon); + + auto finishLat = GetDouble(incomeString, i); + auto finishLon = GetDouble(incomeString, i); + result.stop = mercator::FromLatLon(finishLat, finishLon); +} + class RouteTester { public: @@ -93,12 +129,12 @@ class RouteTester LOG(LINFO, ("Can't build the route. Code:", result.second)); return false; } - auto const delta = record.distance * kRouteLengthAccuracy; + auto const delta = record.distanceM * kRouteLengthAccuracy; auto const routeLength = result.first->GetTotalDistanceMeters(); - if (abs(routeLength - record.distance) < delta) + if (abs(routeLength - record.distanceM) < delta) return true; - LOG(LINFO, ("Route has invalid length. Expected:", record.distance, "have:", routeLength)); + LOG(LINFO, ("Route has invalid length. Expected:", record.distanceM, "have:", routeLength)); return false; } @@ -110,7 +146,7 @@ class RouteTester if (startCountry.m_name != finishCountry.m_name || startCountry.m_name.empty()) return false; - if (record.distance < kMinimumRouteDistanceM) + if (record.distanceM < kMinimumRouteDistanceM) return false; if (m_checkedCountries[startCountry.m_name] > FLAGS_confidence) @@ -153,7 +189,7 @@ class RouteTester map m_errors; }; -void ReadInput(istream & stream, RouteTester & tester) +void ReadInputDist(istream & stream, RouteTester & tester) { string line; while (stream.good()) @@ -175,6 +211,44 @@ void ReadInput(istream & stream, RouteTester & tester) tester.PrintStatistics(); } +void ReadInputTwoThreads(istream & stream) +{ + integration::IRouterComponents & components = integration::GetVehicleComponents(VehicleType::Car); + string line; + while (stream.good()) + { + getline(stream, line); + if (line.empty()) + continue; + + UserRoutingRecord record; + ParseRouteLine(line, record); + LOG(LINFO, (record.start, record.stop)); + + vector, RouterResultCode>> result; + for (bool useTwoThreads : {false, true}) + { + components.GetRouter().ClearState(); + result.push_back(integration::CalculateRoute( + components, record.start, m2::PointD::Zero(), record.stop, useTwoThreads)); + } + CHECK_EQUAL(result[0].second, result[1].second, (line)); + if (result[0].second == RouterResultCode::NoError) + { +// CHECK_EQUAL(result[0].first->GetPoly().GetSize(), result[1].first->GetPoly().GetSize(), +// (line)); +// CHECK_EQUAL(result[0].first->GetTotalDistanceMeters(), +// result[1].first->GetTotalDistanceMeters(), (line)); + for (size_t i = 0; i < result[0].first->GetPoly().GetSize(); ++i) + { + CHECK_EQUAL(result[0].first->GetPoly().GetPoint(i), result[1].first->GetPoly().GetPoint(i), + ("i:", i, line, mercator::ToLatLon(result[0].first->GetPoly().GetPoint(i)), mercator::ToLatLon(result[1].first->GetPoly().GetPoint(i)), + "dist one thread:", result[0].first->GetTotalDistanceMeters(), "dist two threads:", result[1].first->GetTotalDistanceMeters())); + } + } + } +} + int main(int argc, char ** argv) { google::SetUsageMessage("Check mwm and routing files consistency. Calculating roads from a user statistics."); @@ -186,9 +260,20 @@ int main(int argc, char ** argv) if (FLAGS_input_file.empty()) return 1; - RouteTester tester; ifstream stream(FLAGS_input_file); - ReadInput(stream, tester); + if (FLAGS_mode == "dist") + { + RouteTester tester; + ReadInputDist(stream, tester); + } + else if (FLAGS_mode == "two_threads") + { + ReadInputTwoThreads(stream); + } + else + { + return 1; + } return 0; } diff --git a/routing/routing_helpers.cpp b/routing/routing_helpers.cpp index b6ea8a13bd7..f4b2b0b8e6b 100644 --- a/routing/routing_helpers.cpp +++ b/routing/routing_helpers.cpp @@ -245,19 +245,19 @@ bool CheckGraphConnectivity(Segment const & start, bool isOutgoing, bool useRout AStarLengthChecker::AStarLengthChecker(IndexGraphStarter & starter) : m_starter(starter) {} -bool AStarLengthChecker::operator()(RouteWeight const & weight) const +bool AStarLengthChecker::operator()(RouteWeight const & weight, bool isOutgoing) const { - return m_starter.CheckLength(weight); + return m_starter.CheckLength(weight, isOutgoing); } // AdjustLengthChecker ----------------------------------------------------------------------------- AdjustLengthChecker::AdjustLengthChecker(IndexGraphStarter & starter) : m_starter(starter) {} -bool AdjustLengthChecker::operator()(RouteWeight const & weight) const +bool AdjustLengthChecker::operator()(RouteWeight const & weight, bool isOutgoing) const { // Limit of adjust in seconds. double constexpr kAdjustLimitSec = 5 * 60; - return weight <= RouteWeight(kAdjustLimitSec) && m_starter.CheckLength(weight); + return weight <= RouteWeight(kAdjustLimitSec) && m_starter.CheckLength(weight, isOutgoing); } } // namespace routing diff --git a/routing/routing_helpers.hpp b/routing/routing_helpers.hpp index 9ba5fc3d8d4..95a2a5861dc 100644 --- a/routing/routing_helpers.hpp +++ b/routing/routing_helpers.hpp @@ -84,14 +84,14 @@ bool CheckGraphConnectivity(Segment const & start, bool isOutgoing, struct AStarLengthChecker { explicit AStarLengthChecker(IndexGraphStarter & starter); - bool operator()(RouteWeight const & weight) const; + bool operator()(RouteWeight const & weight, bool isOutgoing) const; IndexGraphStarter & m_starter; }; struct AdjustLengthChecker { explicit AdjustLengthChecker(IndexGraphStarter & starter); - bool operator()(RouteWeight const & weight) const; + bool operator()(RouteWeight const & weight, bool isOutgoing) const; IndexGraphStarter & m_starter; }; } // namespace routing diff --git a/routing/routing_integration_tests/get_altitude_test.cpp b/routing/routing_integration_tests/get_altitude_test.cpp index 7df17bc7808..bfb01cda21e 100644 --- a/routing/routing_integration_tests/get_altitude_test.cpp +++ b/routing/routing_integration_tests/get_altitude_test.cpp @@ -44,8 +44,8 @@ void TestAltitudeOfAllMwmFeatures(string const & countryId, TEST_EQUAL(regResult.second, MwmSet::RegResult::Success, ()); TEST(regResult.first.IsAlive(), ()); - unique_ptr altitudeLoader = - make_unique(dataSource, regResult.first /* mwmId */); + unique_ptr altitudeLoader = make_unique( + dataSource, regResult.first /* mwmId */, false /* twoThreadsReady */); ForEachFeature(country.GetPath(MapFileType::Map), [&](FeatureType & f, uint32_t const & id) { if (!routing::IsRoad(TypesHolder(f))) @@ -56,7 +56,8 @@ void TestAltitudeOfAllMwmFeatures(string const & countryId, if (pointsCount == 0) return; - geometry::Altitudes altitudes = altitudeLoader->GetAltitudes(id, pointsCount); + geometry::Altitudes altitudes = + altitudeLoader->GetAltitudes(id, pointsCount, true /* isOutgoing */); TEST(!altitudes.empty(), ("Empty altitude vector. MWM:", countryId, ", feature id:", id, ", altitudes:", altitudes)); diff --git a/routing/routing_integration_tests/pedestrian_route_test.cpp b/routing/routing_integration_tests/pedestrian_route_test.cpp index d366f0f7e83..96508280575 100644 --- a/routing/routing_integration_tests/pedestrian_route_test.cpp +++ b/routing/routing_integration_tests/pedestrian_route_test.cpp @@ -581,3 +581,13 @@ UNIT_TEST(NoTurnOnForkingRoad2) TEST_EQUAL(t[0].m_pedestrianTurn, PedestrianDirection::TurnRight, ()); } + +// Test on two threads A* bidirectional routing. +UNIT_TEST(RussiaMoscowTarusaTwoThreads) +{ + integration::CalculateRouteAndTestRouteLength( + integration::GetVehicleComponents(VehicleType::Pedestrian), + mercator::FromLatLon(55.85779, 37.40948), {0.0, 0.0}, + mercator::FromLatLon(54.71961, 37.19449), 162575.0, 0.07 /* relativeError */, + true /* useTwoThreads */); +} diff --git a/routing/routing_integration_tests/route_test.cpp b/routing/routing_integration_tests/route_test.cpp index d91f3a4f3a7..e9649a8bd25 100644 --- a/routing/routing_integration_tests/route_test.cpp +++ b/routing/routing_integration_tests/route_test.cpp @@ -572,4 +572,15 @@ namespace integration::GetVehicleComponents(VehicleType::Car), mercator::FromLatLon(55.31103, 38.80954), {0., 0.}, mercator::FromLatLon(55.31155, 38.8217), 2489.8); } + + // Test on two threads A* bidirectional routing. + UNIT_TEST(RussiaMoscowStStPetersburgTwoThreads) + { + auto & vehicleComponents = integration::GetVehicleComponents(VehicleType::Car); + + integration::CalculateRouteAndTestRouteLength( + vehicleComponents, mercator::FromLatLon(55.74942, 37.62118), {0.0, 0.0}, + mercator::FromLatLon(59.9394, 30.31492), 706503.0, 0.07 /* relativeError */, + true /* useTwoThreads */); + } } // namespace diff --git a/routing/routing_integration_tests/routing_test_tools.cpp b/routing/routing_integration_tests/routing_test_tools.cpp index eca84aaebd0..ef8731ed17f 100644 --- a/routing/routing_integration_tests/routing_test_tools.cpp +++ b/routing/routing_integration_tests/routing_test_tools.cpp @@ -153,12 +153,13 @@ IRouterComponents & GetVehicleComponents(VehicleType vehicleType) TRouteResult CalculateRoute(IRouterComponents const & routerComponents, m2::PointD const & startPoint, m2::PointD const & startDirection, - m2::PointD const & finalPoint) + m2::PointD const & finalPoint, bool useTwoThreads /* = false */) { RouterDelegate delegate; shared_ptr route = make_shared("mapsme", 0 /* route id */); RouterResultCode result = routerComponents.GetRouter().CalculateRoute( - Checkpoints(startPoint, finalPoint), startDirection, false /* adjust */, delegate, *route); + Checkpoints(startPoint, finalPoint), startDirection, useTwoThreads, false /* adjust */, + delegate, *route); ASSERT(route, ()); routerComponents.GetRouter().SetGuides({}); return TRouteResult(route, result); @@ -171,7 +172,8 @@ TRouteResult CalculateRoute(IRouterComponents const & routerComponents, shared_ptr route = make_shared("mapsme", 0 /* route id */); routerComponents.GetRouter().SetGuides(move(guides)); RouterResultCode result = routerComponents.GetRouter().CalculateRoute( - checkpoints, m2::PointD::Zero() /* startDirection */, false /* adjust */, delegate, *route); + checkpoints, m2::PointD::Zero() /* startDirection */, false /* useTwoThreads */, + false /* adjust */, delegate, *route); ASSERT(route, ()); routerComponents.GetRouter().SetGuides({}); return TRouteResult(route, result); @@ -234,10 +236,10 @@ void CalculateRouteAndTestRouteLength(IRouterComponents const & routerComponents m2::PointD const & startPoint, m2::PointD const & startDirection, m2::PointD const & finalPoint, double expectedRouteMeters, - double relativeError /* = 0.07 */) + double relativeError /* = 0.07 */, bool useTwoThreads /* = false */) { TRouteResult routeResult = - CalculateRoute(routerComponents, startPoint, startDirection, finalPoint); + CalculateRoute(routerComponents, startPoint, startDirection, finalPoint, useTwoThreads); RouterResultCode const result = routeResult.second; TEST_EQUAL(result, RouterResultCode::NoError, ()); CHECK(routeResult.first, ()); diff --git a/routing/routing_integration_tests/routing_test_tools.hpp b/routing/routing_integration_tests/routing_test_tools.hpp index e25de08bf7e..6c8dd3043e1 100644 --- a/routing/routing_integration_tests/routing_test_tools.hpp +++ b/routing/routing_integration_tests/routing_test_tools.hpp @@ -108,7 +108,7 @@ IRouterComponents & GetVehicleComponents(VehicleType vehicleType); TRouteResult CalculateRoute(IRouterComponents const & routerComponents, m2::PointD const & startPoint, m2::PointD const & startDirection, - m2::PointD const & finalPoint); + m2::PointD const & finalPoint, bool useTwoThreads = false); TRouteResult CalculateRoute(IRouterComponents const & routerComponents, Checkpoints const & checkpoints, GuidesTracks && guides); @@ -129,7 +129,7 @@ void CalculateRouteAndTestRouteLength(IRouterComponents const & routerComponents m2::PointD const & startPoint, m2::PointD const & startDirection, m2::PointD const & finalPoint, double expectedRouteMeters, - double relativeError = 0.07); + double relativeError = 0.07, bool useTwoThreads = false); void CalculateRouteAndTestRouteTime(IRouterComponents const & routerComponents, m2::PointD const & startPoint, diff --git a/routing/routing_tests/astar_algorithm_test.cpp b/routing/routing_tests/astar_algorithm_test.cpp index 0c9ed4edeaa..cb0d368b373 100644 --- a/routing/routing_tests/astar_algorithm_test.cpp +++ b/routing/routing_tests/astar_algorithm_test.cpp @@ -18,6 +18,14 @@ using namespace std; using Algorithm = AStarAlgorithm; +void TestOnRouteGeomAndDistance( + RoutingResult const & actualRoute, + vector const & expectedRoute, double const & expectedDistance) +{ + TEST_EQUAL(expectedRoute, actualRoute.m_path, ()); + TEST_ALMOST_EQUAL_ULPS(expectedDistance, actualRoute.m_distance, ()); +} + void TestAStar(UndirectedGraph & graph, vector const & expectedRoute, double const & expectedDistance) { Algorithm algo; @@ -25,15 +33,23 @@ void TestAStar(UndirectedGraph & graph, vector const & expectedRoute, Algorithm::ParamsForTests<> params(graph, 0u /* startVertex */, 4u /* finishVertex */, nullptr /* prevRoute */); + // Algorithm::FindPath() testing. RoutingResult actualRoute; TEST_EQUAL(Algorithm::Result::OK, algo.FindPath(params, actualRoute), ()); - TEST_EQUAL(expectedRoute, actualRoute.m_path, ()); - TEST_ALMOST_EQUAL_ULPS(expectedDistance, actualRoute.m_distance, ()); + TestOnRouteGeomAndDistance(actualRoute, expectedRoute, expectedDistance); + // Algorithm::FindPathBidirectional() in one thread testing. actualRoute.m_path.clear(); - TEST_EQUAL(Algorithm::Result::OK, algo.FindPathBidirectional(params, actualRoute), ()); - TEST_EQUAL(expectedRoute, actualRoute.m_path, ()); - TEST_ALMOST_EQUAL_ULPS(expectedDistance, actualRoute.m_distance, ()); + TEST_EQUAL(Algorithm::Result::OK, + algo.FindPathBidirectional(params, actualRoute), ()); + TestOnRouteGeomAndDistance(actualRoute, expectedRoute, expectedDistance); + + // Algorithm::FindPathBidirectional() in two thread testing. + actualRoute.m_path.clear(); + graph.SetTwoThreadsReady(true); + TEST_EQUAL(Algorithm::Result::OK, + algo.FindPathBidirectional(params, actualRoute), ()); + TestOnRouteGeomAndDistance(actualRoute, expectedRoute, expectedDistance); } UNIT_TEST(AStarAlgorithm_Sample) @@ -63,7 +79,7 @@ UNIT_TEST(AStarAlgorithm_CheckLength) graph.AddEdge(2, 4, 10); graph.AddEdge(3, 4, 3); - auto checkLength = [](double weight) { return weight < 23; }; + auto checkLength = [](double weight, bool /* isOutgoing */) { return weight < 23; }; Algorithm algo; Algorithm::ParamsForTests params( graph, 0u /* startVertex */, 4u /* finishVertex */, nullptr /* prevRoute */, @@ -94,7 +110,7 @@ UNIT_TEST(AdjustRoute) // Each edge contains {vertexId, weight}. vector const prevRoute = {{0, 0}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}}; - auto checkLength = [](double weight) { return weight <= 1.0; }; + auto checkLength = [](double weight, bool /* isOutgoing */) { return weight <= 1.0; }; Algorithm algo; Algorithm::ParamsForTests params( graph, 6 /* startVertex */, {} /* finishVertex */, &prevRoute, move(checkLength)); @@ -118,7 +134,7 @@ UNIT_TEST(AdjustRouteNoPath) // Each edge contains {vertexId, weight}. vector const prevRoute = {{0, 0}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}}; - auto checkLength = [](double weight) { return weight <= 1.0; }; + auto checkLength = [](double weight, bool /* isOutgoing */) { return weight <= 1.0; }; Algorithm algo; Algorithm::ParamsForTests params(graph, 6 /* startVertex */, {} /* finishVertex */, &prevRoute, move(checkLength)); @@ -141,7 +157,7 @@ UNIT_TEST(AdjustRouteOutOfLimit) // Each edge contains {vertexId, weight}. vector const prevRoute = {{0, 0}, {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}}; - auto checkLength = [](double weight) { return weight <= 1.0; }; + auto checkLength = [](double weight, bool /* isOutgoing */) { return weight <= 1.0; }; Algorithm algo; Algorithm::ParamsForTests params( graph, 6 /* startVertex */, {} /* finishVertex */, &prevRoute, move(checkLength)); diff --git a/routing/routing_tests/async_router_test.cpp b/routing/routing_tests/async_router_test.cpp index 0d0cf7b840b..bb6af047eeb 100644 --- a/routing/routing_tests/async_router_test.cpp +++ b/routing/routing_tests/async_router_test.cpp @@ -37,9 +37,10 @@ class DummyRouter : public IRouter // IRouter overrides: string GetName() const override { return "Dummy"; } void SetGuides(GuidesTracks && /* guides */) override {} - RouterResultCode CalculateRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, - bool adjustToPrevRoute, RouterDelegate const & delegate, - Route & route) override + RouterResultCode CalculateRoute(Checkpoints const & checkpoints, + m2::PointD const & startDirection, bool useTwoThreads, + bool adjustToPrevRoute, RouterDelegate const & delegate, + Route & route) override { route = Route("dummy", checkpoints.GetPoints().cbegin(), checkpoints.GetPoints().cend(), 0 /* route id */); diff --git a/routing/routing_tests/index_graph_test.cpp b/routing/routing_tests/index_graph_test.cpp index 759948b037b..c9544ecdd44 100644 --- a/routing/routing_tests/index_graph_test.cpp +++ b/routing/routing_tests/index_graph_test.cpp @@ -81,7 +81,8 @@ void TestRoute(FakeEnding const & start, FakeEnding const & finish, size_t expec void TestEdges(IndexGraph & graph, Segment const & segment, vector const & expectedTargets, bool isOutgoing) { - ASSERT(segment.IsForward() || !graph.GetGeometry().GetRoad(segment.GetFeatureId()).IsOneWay(), + ASSERT(segment.IsForward() || + !graph.GetGeometry().GetRoad(segment.GetFeatureId(), true /* isOutgoing */).IsOneWay(), ()); vector edges; diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index c25f645a31e..e5d405ad81f 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -8,7 +8,6 @@ #include "transit/transit_version.hpp" -#include "base/assert.hpp" #include "base/math.hpp" #include @@ -85,13 +84,15 @@ void NoUTurnRestrictionTest::TestRouteGeom(Segment const & start, Segment const nullptr /* prevRoute */); RoutingResult routingResult; - auto const resultCode = algorithm.FindPathBidirectional(params, routingResult); + auto const resultCode = + algorithm.FindPathBidirectional(params, routingResult); TEST_EQUAL(resultCode, expectedRouteResult, ()); for (size_t i = 0; i < routingResult.m_path.size(); ++i) { static auto constexpr kEps = 1e-3; - auto const point = m_graph->GetWorldGraph().GetPoint(routingResult.m_path[i], true /* forward */); + auto const point = m_graph->GetWorldGraph().GetPoint(routingResult.m_path[i], true /* front */, + true /* isOutgoing */); if (!base::AlmostEqualAbs(mercator::FromLatLon(point), expectedRouteGeom[i], kEps)) { TEST(false, ("Coords missmated at index:", i, "expected:", expectedRouteGeom[i], @@ -101,8 +102,10 @@ void NoUTurnRestrictionTest::TestRouteGeom(Segment const & start, Segment const } // ZeroGeometryLoader ------------------------------------------------------------------------------ -void ZeroGeometryLoader::Load(uint32_t /* featureId */, routing::RoadGeometry & road) +void ZeroGeometryLoader::Load(uint32_t /* featureId */, routing::RoadGeometry & road, + bool isOutgoing) { + CHECK(isOutgoing, ("ZeroGeometryLoader() is not ready for two threads feature parsing.")); // Any valid road will do. auto const points = routing::RoadGeometry::Points({{0.0, 0.0}, {0.0, 1.0}}); road = RoadGeometry(true /* oneWay */, 1.0 /* weightSpeedKMpH */, 1.0 /* etaSpeedKMpH */, points); @@ -275,7 +278,8 @@ bool TestIndexGraphTopology::FindPath(Vertex start, Vertex finish, double & path AlgorithmForWorldGraph::ParamsForTests<> params(graphForAStar, startSegment, finishSegment, nullptr /* prevRoute */); RoutingResult routingResult; - auto const resultCode = algorithm.FindPathBidirectional(params, routingResult); + auto const resultCode = + algorithm.FindPathBidirectional(params, routingResult); // Check unidirectional AStar returns same result. { @@ -420,7 +424,8 @@ unique_ptr BuildWorldGraph(unique_ptr(); indexLoader->AddGraph(kTestNumMwmId, move(graph)); return make_unique(nullptr /* crossMwmGraph */, move(indexLoader), - estimator, MwmHierarchyHandler(nullptr, nullptr)); + estimator, + MwmHierarchyHandler(nullptr, nullptr)); } unique_ptr BuildIndexGraph(unique_ptr geometryLoader, @@ -437,7 +442,7 @@ unique_ptr BuildWorldGraph(unique_ptr const & joints) { auto graph = make_unique(make_shared(move(geometryLoader)), estimator); - + graph->Import(joints); auto indexLoader = make_unique(); indexLoader->AddGraph(kTestNumMwmId, move(graph)); @@ -479,7 +484,8 @@ AlgorithmForWorldGraph::Result CalculateRoute(IndexGraphStarter & starter, vecto starter, starter.GetStartSegment(), starter.GetFinishSegment(), nullptr /* prevRoute */, AStarLengthChecker(starter)); - auto const resultCode = algorithm.FindPathBidirectional(params, routingResult); + auto const resultCode = + algorithm.FindPathBidirectional(params, routingResult); timeSec = routingResult.m_distance.GetWeight(); roadPoints = routingResult.m_path; @@ -518,13 +524,13 @@ void TestRouteGeometry(IndexGraphStarter & starter, for (auto const & routeSeg : routeSegs) { - auto const & ll = starter.GetPoint(routeSeg, false /* front */); + auto const & ll = starter.GetPoint(routeSeg, false /* front */, true /* isOutgoing */); // Note. In case of A* router all internal points of route are duplicated. // So it's necessary to exclude the duplicates. pushPoint(ll); } - pushPoint(starter.GetPoint(routeSegs.back(), false /* front */)); + pushPoint(starter.GetPoint(routeSegs.back(), false /* front */, true /* isOutgoing */)); TEST_EQUAL(geom.size(), expectedRouteGeom.size(), ("geom:", geom, "expectedRouteGeom:", expectedRouteGeom)); for (size_t i = 0; i < geom.size(); ++i) { @@ -582,8 +588,8 @@ void TestRestrictions(double expectedLength, double length = 0.0; for (auto const & segment : segments) { - auto const back = mercator::FromLatLon(starter.GetPoint(segment, false /* front */)); - auto const front = mercator::FromLatLon(starter.GetPoint(segment, true /* front */)); + auto const back = mercator::FromLatLon(starter.GetPoint(segment, false /* front */, true /* isOutgoing */)); + auto const front = mercator::FromLatLon(starter.GetPoint(segment, true /* front */, true /* isOutgoing */)); length += back.Length(front); } diff --git a/routing/routing_tests/index_graph_tools.hpp b/routing/routing_tests/index_graph_tools.hpp index adba903d74d..b5aec943a74 100644 --- a/routing/routing_tests/index_graph_tools.hpp +++ b/routing/routing_tests/index_graph_tools.hpp @@ -28,6 +28,8 @@ #include "geometry/point2d.hpp" +#include "base/assert.hpp" + #include #include #include @@ -50,15 +52,19 @@ using AlgorithmForWorldGraph = AStarAlgorithm class WorldGraphForAStar : public AStarGraph { public: - explicit WorldGraphForAStar(std::unique_ptr graph) : m_graph(std::move(graph)) {} + explicit WorldGraphForAStar(std::unique_ptr graph) + : m_graph(std::move(graph)) + { + CHECK(!IsTwoThreadsReady(), ()); + } ~WorldGraphForAStar() override = default; // AStarGraph overrides: // @{ - Weight HeuristicCostEstimate(Vertex const & from, Vertex const & to) override + Weight HeuristicCostEstimate(Vertex const & from, Vertex const & to, bool isOutgoing) override { - return m_graph->HeuristicCostEstimate(m_graph->GetPoint(from, true /* front */), - m_graph->GetPoint(to, true /* front */)); + return m_graph->HeuristicCostEstimate(m_graph->GetPoint(from, true /* front */, isOutgoing), + m_graph->GetPoint(to, true /* front */, isOutgoing)); } void GetOutgoingEdgesList(astar::VertexData const & vertexData, @@ -119,7 +125,7 @@ class ZeroGeometryLoader final : public routing::GeometryLoader // GeometryLoader overrides: ~ZeroGeometryLoader() override = default; - void Load(uint32_t featureId, routing::RoadGeometry & road) override; + void Load(uint32_t featureId, routing::RoadGeometry & road, bool isOutgoing) override; }; class TestIndexGraphLoader final : public IndexGraphLoader @@ -137,6 +143,8 @@ class TestIndexGraphLoader final : public IndexGraphLoader void Clear() override; + bool IsTwoThreadsReady() const override { return false; } + void AddGraph(NumMwmId mwmId, std::unique_ptr graph); private: diff --git a/routing/routing_tests/routing_algorithm.cpp b/routing/routing_tests/routing_algorithm.cpp index 5f023a6a3c6..fc5875e4768 100644 --- a/routing/routing_tests/routing_algorithm.cpp +++ b/routing/routing_tests/routing_algorithm.cpp @@ -16,6 +16,10 @@ namespace routing_test { +UndirectedGraph::UndirectedGraph() +{ +} + void UndirectedGraph::AddEdge(Vertex u, Vertex v, Weight w) { m_adjs[u].emplace_back(v, w); @@ -45,7 +49,7 @@ void UndirectedGraph::GetOutgoingEdgesList(astar::VertexData con GetEdgesList(vertexData.m_vertex, true /* isOutgoing */, adj); } -double UndirectedGraph::HeuristicCostEstimate(Vertex const & v, Vertex const & w) +double UndirectedGraph::HeuristicCostEstimate(Vertex const & v, Vertex const & w, bool isOutgoing) { return 0.0; } @@ -115,7 +119,9 @@ class RoadGraph : public Algorithm::Graph explicit RoadGraph(IRoadGraph const & roadGraph) : m_roadGraph(roadGraph), m_maxSpeedMPS(KMPH2MPS(roadGraph.GetMaxSpeedKMpH())) - {} + { + CHECK(!IsTwoThreadsReady(), ()); + } void GetOutgoingEdgesList(astar::VertexData const & vertexData, std::vector & adj) override @@ -157,7 +163,7 @@ class RoadGraph : public Algorithm::Graph } } - double HeuristicCostEstimate(Vertex const & v, Vertex const & w) override + double HeuristicCostEstimate(Vertex const & v, Vertex const & w, bool /* isOutgoing */) override { return TimeBetweenSec(v, w, m_maxSpeedMPS); } diff --git a/routing/routing_tests/routing_algorithm.hpp b/routing/routing_tests/routing_algorithm.hpp index 9057279e09b..aa02e027401 100644 --- a/routing/routing_tests/routing_algorithm.hpp +++ b/routing/routing_tests/routing_algorithm.hpp @@ -30,6 +30,8 @@ struct SimpleEdge class UndirectedGraph : public AStarGraph { public: + UndirectedGraph(); + void AddEdge(Vertex u, Vertex v, Weight w); size_t GetNodesNumber() const; @@ -39,15 +41,21 @@ class UndirectedGraph : public AStarGraph std::vector & adj) override; void GetOutgoingEdgesList(astar::VertexData const & vertexData, std::vector & adj) override; - double HeuristicCostEstimate(Vertex const & v, Vertex const & w) override; + double HeuristicCostEstimate(Vertex const & v, Vertex const & w, bool isOutgoing) override; + virtual bool IsTwoThreadsReady() const override { return m_twoThreadsReady; } // @} void GetEdgesList(Vertex const & vertex, bool /* isOutgoing */, std::vector & adj); + /// \note If |twoThreadsRead| is equal to true it means class should be ready that methods + /// GetIngoingEdgesList(), GetOutgoingEdgesList() and HeuristicCostEstimate() are called + /// from two threads depending on |isOutgoing| parameter. + void SetTwoThreadsReady(bool twoThreadsRead) { m_twoThreadsReady = twoThreadsRead; } private: void GetAdjacencyList(Vertex v, std::vector & adj) const; std::map> m_adjs; + bool m_twoThreadsReady = false; }; class DirectedGraph diff --git a/routing/routing_tests/routing_session_test.cpp b/routing/routing_tests/routing_session_test.cpp index 206c1cf204e..4ea1db84ddc 100644 --- a/routing/routing_tests/routing_session_test.cpp +++ b/routing/routing_tests/routing_session_test.cpp @@ -58,9 +58,8 @@ class DummyRouter : public IRouter void ClearState() override {} void SetGuides(GuidesTracks && /* guides */) override {} - RouterResultCode CalculateRoute(Checkpoints const & /* checkpoints */, - m2::PointD const & /* startDirection */, bool /* adjust */, - RouterDelegate const & /* delegate */, Route & route) override + RouterResultCode CalculateRoute(Checkpoints const & /* checkpoints */, m2::PointD const & /* startDirection */, bool /* useTwoThreads */, + bool /* adjust */, RouterDelegate const & /* delegate */, Route & route) override { ++m_buildCount; route = m_route; @@ -90,8 +89,9 @@ class ReturnCodesRouter : public IRouter void SetGuides(GuidesTracks && /* guides */) override {} RouterResultCode CalculateRoute(Checkpoints const & /* checkpoints */, - m2::PointD const & /* startDirection */, bool /* adjust */, - RouterDelegate const & /* delegate */, Route & route) override + m2::PointD const & /* startDirection */, bool /* useTwoThreads */, + bool /* adjust */, RouterDelegate const & /* delegate */, + Route & route) override { TEST_LESS(m_returnCodesIdx, m_returnCodes.size(), ()); route = Route(GetName(), m_route.begin(), m_route.end(), 0 /* route id */); diff --git a/routing/single_vehicle_world_graph.cpp b/routing/single_vehicle_world_graph.cpp index eb40b9fccc5..af26a9abea8 100644 --- a/routing/single_vehicle_world_graph.cpp +++ b/routing/single_vehicle_world_graph.cpp @@ -2,6 +2,8 @@ #include "routing/base/astar_algorithm.hpp" +#include "base/optional_lock_guard.hpp" + #include #include @@ -25,6 +27,8 @@ SingleVehicleWorldGraph::SingleVehicleWorldGraph(unique_ptr cross MwmHierarchyHandler && hierarchyHandler) : m_crossMwmGraph(move(crossMwmGraph)) , m_loader(move(loader)) + , m_crossMwmGraphMtx(m_loader->IsTwoThreadsReady() ? std::make_optional() + : std::nullopt) , m_estimator(move(estimator)) , m_hierarchyHandler(std::move(hierarchyHandler)) { @@ -37,6 +41,10 @@ void SingleVehicleWorldGraph::CheckAndProcessTransitFeatures(Segment const & par vector & parentWeights, bool isOutgoing) { + // Synchronization note. While building route with the help of two thread bidirectional A* + // SingleVehicleWorldGraph::CheckAndProcessTransitFeatures() may be called from two + // threads (depending on the value of |isOutgoing|). According to performance tests + // there's almost no performance leak after locking this mutex here. bool opposite = !isOutgoing; vector newCrossMwmEdges; @@ -48,20 +56,27 @@ void SingleVehicleWorldGraph::CheckAndProcessTransitFeatures(Segment const & par NumMwmId const edgeMwmId = target.GetMwmId(); - if (!m_crossMwmGraph->IsFeatureTransit(edgeMwmId, target.GetFeatureId())) - continue; + { + base::OptionalLockGuard guard(m_crossMwmGraphMtx); + if (!m_crossMwmGraph->IsFeatureTransit(edgeMwmId, target.GetFeatureId())) + continue; + } auto & currentIndexGraph = GetIndexGraph(mwmId); vector twins; - m_crossMwmGraph->GetTwinFeature(target.GetSegment(true /* start */), isOutgoing, twins); + { + base::OptionalLockGuard guard(m_crossMwmGraphMtx); + m_crossMwmGraph->GetTwinFeature(target.GetSegment(true /* start */), isOutgoing, twins); + } for (auto const & twin : twins) { NumMwmId const twinMwmId = twin.GetMwmId(); uint32_t const twinFeatureId = twin.GetFeatureId(); - Segment const start(twinMwmId, twinFeatureId, target.GetSegmentId(!opposite), target.IsForward()); + Segment const start(twinMwmId, twinFeatureId, target.GetSegmentId(!opposite), + target.IsForward()); auto & twinIndexGraph = GetIndexGraph(twinMwmId); @@ -69,15 +84,17 @@ void SingleVehicleWorldGraph::CheckAndProcessTransitFeatures(Segment const & par twinIndexGraph.GetLastPointsForJoint({start}, isOutgoing, lastPoints); ASSERT_EQUAL(lastPoints.size(), 1, ()); - if (auto edge = currentIndexGraph.GetJointEdgeByLastPoint(parent, - target.GetSegment(!opposite), - isOutgoing, lastPoints.back())) + if (auto edge = currentIndexGraph.GetJointEdgeByLastPoint( + parent, target.GetSegment(!opposite), isOutgoing, lastPoints.back())) { newCrossMwmEdges.emplace_back(*edge); newCrossMwmEdges.back().GetTarget().SetFeatureId(twinFeatureId); newCrossMwmEdges.back().GetTarget().SetMwmId(twinMwmId); - newCrossMwmEdges.back().GetWeight() += - m_hierarchyHandler.GetCrossBorderPenalty(mwmId, twinMwmId); + { + base::OptionalLockGuard guard(m_crossMwmGraphMtx); + newCrossMwmEdges.back().GetWeight() += + m_hierarchyHandler.GetCrossBorderPenalty(mwmId, twinMwmId); + } parentWeights.emplace_back(parentWeights[i]); } @@ -114,7 +131,7 @@ void SingleVehicleWorldGraph::GetEdgeList( if (!parent.IsRealSegment()) return; - ASSERT(m_parentsForJoints.forward && m_parentsForJoints.backward, + ASSERT(isOutgoing ? m_parentsForJoints.forward : m_parentsForJoints.backward, ("m_parentsForJoints was not initialized in SingleVehicleWorldGraph.")); auto & parents = isOutgoing ? *m_parentsForJoints.forward : *m_parentsForJoints.backward; auto & indexGraph = GetIndexGraph(parent.GetMwmId()); @@ -125,25 +142,26 @@ void SingleVehicleWorldGraph::GetEdgeList( } LatLonWithAltitude const & SingleVehicleWorldGraph::GetJunction(Segment const & segment, - bool front) + bool front, bool isOutgoing) { - return GetRoadGeometry(segment.GetMwmId(), segment.GetFeatureId()) + return GetRoadGeometry(segment.GetMwmId(), segment.GetFeatureId(), isOutgoing) .GetJunction(segment.GetPointId(front)); } -ms::LatLon const & SingleVehicleWorldGraph::GetPoint(Segment const & segment, bool front) +ms::LatLon const & SingleVehicleWorldGraph::GetPoint(Segment const & segment, bool front, + bool isOutgoing) { - return GetJunction(segment, front).GetLatLon(); + return GetJunction(segment, front, isOutgoing).GetLatLon(); } -bool SingleVehicleWorldGraph::IsOneWay(NumMwmId mwmId, uint32_t featureId) +bool SingleVehicleWorldGraph::IsOneWay(NumMwmId mwmId, uint32_t featureId, bool isOutgoing) { - return GetRoadGeometry(mwmId, featureId).IsOneWay(); + return GetRoadGeometry(mwmId, featureId, isOutgoing).IsOneWay(); } -bool SingleVehicleWorldGraph::IsPassThroughAllowed(NumMwmId mwmId, uint32_t featureId) +bool SingleVehicleWorldGraph::IsPassThroughAllowed(NumMwmId mwmId, uint32_t featureId, bool isOutgoing) { - return GetRoadGeometry(mwmId, featureId).IsPassThroughAllowed(); + return GetRoadGeometry(mwmId, featureId, isOutgoing).IsPassThroughAllowed(); } RouteWeight SingleVehicleWorldGraph::HeuristicCostEstimate(ms::LatLon const & from, @@ -153,11 +171,11 @@ RouteWeight SingleVehicleWorldGraph::HeuristicCostEstimate(ms::LatLon const & fr } -RouteWeight SingleVehicleWorldGraph::CalcSegmentWeight(Segment const & segment, +RouteWeight SingleVehicleWorldGraph::CalcSegmentWeight(Segment const & segment, bool isOutgoing, EdgeEstimator::Purpose purpose) { return RouteWeight(m_estimator->CalcSegmentWeight( - segment, GetRoadGeometry(segment.GetMwmId(), segment.GetFeatureId()), purpose)); + segment, GetRoadGeometry(segment.GetMwmId(), segment.GetFeatureId(), isOutgoing), purpose)); } RouteWeight SingleVehicleWorldGraph::CalcLeapWeight(ms::LatLon const & from, @@ -173,20 +191,21 @@ RouteWeight SingleVehicleWorldGraph::CalcOffroadWeight(ms::LatLon const & from, return RouteWeight(m_estimator->CalcOffroad(from, to, purpose)); } -double SingleVehicleWorldGraph::CalculateETA(Segment const & from, Segment const & to) +double SingleVehicleWorldGraph::CalculateETA(Segment const & from, Segment const & to, bool isOutgoing) { if (from.GetMwmId() != to.GetMwmId()) - return CalculateETAWithoutPenalty(to); + return CalculateETAWithoutPenalty(to, isOutgoing); auto & indexGraph = m_loader->GetIndexGraph(from.GetMwmId()); - return indexGraph.CalculateEdgeWeight(EdgeEstimator::Purpose::ETA, true /* isOutgoing */, from, to).GetWeight(); + return indexGraph.CalculateEdgeWeight(EdgeEstimator::Purpose::ETA, from, to, isOutgoing) + .GetWeight(); } -double SingleVehicleWorldGraph::CalculateETAWithoutPenalty(Segment const & segment) +double SingleVehicleWorldGraph::CalculateETAWithoutPenalty(Segment const & segment, bool isOutgoing) { - return m_estimator->CalcSegmentWeight(segment, - GetRoadGeometry(segment.GetMwmId(), segment.GetFeatureId()), - EdgeEstimator::Purpose::ETA); + return m_estimator->CalcSegmentWeight( + segment, GetRoadGeometry(segment.GetMwmId(), segment.GetFeatureId(), isOutgoing), + EdgeEstimator::Purpose::ETA); } vector const & SingleVehicleWorldGraph::GetTransitions(NumMwmId numMwmId, bool isEnter) @@ -202,9 +221,10 @@ vector SingleVehicleWorldGraph::GetSpeedCamInfo(Segme return m_loader->GetSpeedCameraInfo(segment); } -RoadGeometry const & SingleVehicleWorldGraph::GetRoadGeometry(NumMwmId mwmId, uint32_t featureId) +RoadGeometry const & SingleVehicleWorldGraph::GetRoadGeometry(NumMwmId mwmId, uint32_t featureId, + bool isOutgoing) { - return m_loader->GetGeometry(mwmId).GetRoad(featureId); + return m_loader->GetGeometry(mwmId).GetRoad(featureId, isOutgoing); } void SingleVehicleWorldGraph::GetTwinsInner(Segment const & segment, bool isOutgoing, @@ -213,17 +233,17 @@ void SingleVehicleWorldGraph::GetTwinsInner(Segment const & segment, bool isOutg m_crossMwmGraph->GetTwins(segment, isOutgoing, twins); } -bool SingleVehicleWorldGraph::IsRoutingOptionsGood(Segment const & segment) +bool SingleVehicleWorldGraph::IsRoutingOptionsGood(Segment const & segment, bool isOutgoing) { - auto const & geometry = GetRoadGeometry(segment.GetMwmId(), segment.GetFeatureId()); + auto const & geometry = GetRoadGeometry(segment.GetMwmId(), segment.GetFeatureId(), isOutgoing); return geometry.SuitableForOptions(m_avoidRoutingOptions); } -RoutingOptions SingleVehicleWorldGraph::GetRoutingOptions(Segment const & segment) +RoutingOptions SingleVehicleWorldGraph::GetRoutingOptions(Segment const & segment, bool isOutgoing) { ASSERT(segment.IsRealSegment(), ()); - auto const & geometry = GetRoadGeometry(segment.GetMwmId(), segment.GetFeatureId()); + auto const & geometry = GetRoadGeometry(segment.GetMwmId(), segment.GetFeatureId(), isOutgoing); return geometry.GetRoutingOptions(); } diff --git a/routing/single_vehicle_world_graph.hpp b/routing/single_vehicle_world_graph.hpp index 2b4fad49ed1..1b698514976 100644 --- a/routing/single_vehicle_world_graph.hpp +++ b/routing/single_vehicle_world_graph.hpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include namespace routing @@ -43,6 +45,8 @@ class SingleVehicleWorldGraph final : public WorldGraph bool useRoutingOptions, bool useAccessConditional, std::vector & edges) override; + /// \note This method may be called from two threads. One with |isOutgoing| == true + /// and another one with |isOutgoing| == false. void GetEdgeList(astar::VertexData const & parentVertexData, Segment const & parent, bool isOutgoing, bool useAccessConditional, std::vector & jointEdges, @@ -50,31 +54,36 @@ class SingleVehicleWorldGraph final : public WorldGraph bool CheckLength(RouteWeight const &, double) const override { return true; } - LatLonWithAltitude const & GetJunction(Segment const & segment, bool front) override; - ms::LatLon const & GetPoint(Segment const & segment, bool front) override; + LatLonWithAltitude const & GetJunction(Segment const & segment, bool front, + bool isOutgoing) override; + ms::LatLon const & GetPoint(Segment const & segment, bool front, bool isOutgoing) override; - bool IsOneWay(NumMwmId mwmId, uint32_t featureId) override; - bool IsPassThroughAllowed(NumMwmId mwmId, uint32_t featureId) override; + bool IsOneWay(NumMwmId mwmId, uint32_t featureId, bool isOutgoing) override; + bool IsPassThroughAllowed(NumMwmId mwmId, uint32_t featureId, bool isOutgoing) override; void ClearCachedGraphs() override { m_loader->Clear(); } void SetMode(WorldGraphMode mode) override { m_mode = mode; } WorldGraphMode GetMode() const override { return m_mode; } + /// \note Despite the fact the method is not constant it does not change the state + // of SingleVehicleWorldGraph and may be called from different threads without + // synchronization. RouteWeight HeuristicCostEstimate(ms::LatLon const & from, ms::LatLon const & to) override; - RouteWeight CalcSegmentWeight(Segment const & segment, EdgeEstimator::Purpose purpose) override; + RouteWeight CalcSegmentWeight(Segment const & segment, bool isOutgoing, + EdgeEstimator::Purpose purpose) override; RouteWeight CalcLeapWeight(ms::LatLon const & from, ms::LatLon const & to) const override; RouteWeight CalcOffroadWeight(ms::LatLon const & from, ms::LatLon const & to, EdgeEstimator::Purpose purpose) const override; - double CalculateETA(Segment const & from, Segment const & to) override; - double CalculateETAWithoutPenalty(Segment const & segment) override; + double CalculateETA(Segment const & from, Segment const & to, bool isOutgoing) override; + double CalculateETAWithoutPenalty(Segment const & segment, bool isOutgoing) override; std::vector const & GetTransitions(NumMwmId numMwmId, bool isEnter) override; void SetRoutingOptions(RoutingOptions routingOptions) override { m_avoidRoutingOptions = routingOptions; } /// \returns true if feature, associated with segment satisfies users conditions. - bool IsRoutingOptionsGood(Segment const & segment) override; - RoutingOptions GetRoutingOptions(Segment const & segment) override; + bool IsRoutingOptionsGood(Segment const & segment, bool isOutgoing) override; + RoutingOptions GetRoutingOptions(Segment const & segment, bool isOutgoing) override; std::unique_ptr GetTransitInfo(Segment const & segment) override; std::vector GetSpeedCamInfo(Segment const & segment) override; @@ -94,6 +103,7 @@ class SingleVehicleWorldGraph final : public WorldGraph bool AreWavesConnectible(Parents & forwardParents, JointSegment const & commonVertex, Parents & backwardParents, std::function && fakeFeatureConverter) override; + bool IsTwoThreadsReady() const override { return m_loader->IsTwoThreadsReady(); } // @} // This method should be used for tests only @@ -124,10 +134,15 @@ class SingleVehicleWorldGraph final : public WorldGraph // WorldGraph overrides: void GetTwinsInner(Segment const & s, bool isOutgoing, std::vector & twins) override; - RoadGeometry const & GetRoadGeometry(NumMwmId mwmId, uint32_t featureId); + RoadGeometry const & GetRoadGeometry(NumMwmId mwmId, uint32_t featureId, bool isOutgoing); std::unique_ptr m_crossMwmGraph; std::unique_ptr m_loader; + // This mutex synchronizes the access to cross mwm graph |m_crossMwmGraph|. For the time being + // the synchronization is done only for + // GetEdgeList(astar::VertexData const & ....). + // That means only for SingleVehicleWorldGraph::CheckAndProcessTransitFeatures(...). + std::optional m_crossMwmGraphMtx; std::shared_ptr m_estimator; RoutingOptions m_avoidRoutingOptions = RoutingOptions(); WorldGraphMode m_mode = WorldGraphMode::NoLeaps; diff --git a/routing/transit_world_graph.cpp b/routing/transit_world_graph.cpp index 4f54f1fa018..59121a0ec78 100644 --- a/routing/transit_world_graph.cpp +++ b/routing/transit_world_graph.cpp @@ -38,8 +38,10 @@ void TransitWorldGraph::GetEdgeList(astar::VertexData cons Segment real; if (transitGraph.FindReal(segment, real)) { - bool const haveSameFront = GetJunction(segment, true /* front */) == GetJunction(real, true); - bool const haveSameBack = GetJunction(segment, false /* front */) == GetJunction(real, false); + bool const haveSameFront = GetJunction(segment, true /* front */, isOutgoing) == + GetJunction(real, true /* front */, isOutgoing); + bool const haveSameBack = GetJunction(segment, false /* front */, isOutgoing) == + GetJunction(real, false, isOutgoing); if ((isOutgoing && haveSameFront) || (!isOutgoing && haveSameBack)) { astar::VertexData const data(real, vertexData.m_realDistance); @@ -60,8 +62,10 @@ void TransitWorldGraph::GetEdgeList(astar::VertexData cons auto const & edgeSegment = edge.GetTarget(); for (auto const & s : transitGraph.GetFake(edgeSegment)) { - bool const haveSameFront = GetJunction(edgeSegment, true /* front */) == GetJunction(s, true); - bool const haveSameBack = GetJunction(edgeSegment, false /* front */) == GetJunction(s, false); + bool const haveSameFront = GetJunction(edgeSegment, true /* front */, isOutgoing) == + GetJunction(s, true, isOutgoing); + bool const haveSameBack = GetJunction(edgeSegment, false /* front */, isOutgoing) == + GetJunction(s, false, isOutgoing); if ((isOutgoing && haveSameBack) || (!isOutgoing && haveSameFront)) fakeFromReal.emplace_back(s, edge.GetWeight()); } @@ -77,32 +81,33 @@ void TransitWorldGraph::GetEdgeList( CHECK(false, ("TransitWorldGraph does not support Joints mode.")); } -LatLonWithAltitude const & TransitWorldGraph::GetJunction(Segment const & segment, bool front) +LatLonWithAltitude const & TransitWorldGraph::GetJunction(Segment const & segment, bool front, + bool isOutgoing) { if (TransitGraph::IsTransitSegment(segment)) return GetTransitGraph(segment.GetMwmId()).GetJunction(segment, front); - return GetRealRoadGeometry(segment.GetMwmId(), segment.GetFeatureId()) + return GetRealRoadGeometry(segment.GetMwmId(), segment.GetFeatureId(), isOutgoing) .GetJunction(segment.GetPointId(front)); } -ms::LatLon const & TransitWorldGraph::GetPoint(Segment const & segment, bool front) +ms::LatLon const & TransitWorldGraph::GetPoint(Segment const & segment, bool front, bool isOutgoing) { - return GetJunction(segment, front).GetLatLon(); + return GetJunction(segment, front, isOutgoing).GetLatLon(); } -bool TransitWorldGraph::IsOneWay(NumMwmId mwmId, uint32_t featureId) +bool TransitWorldGraph::IsOneWay(NumMwmId mwmId, uint32_t featureId, bool isOutgoing) { if (TransitGraph::IsTransitFeature(featureId)) return true; - return GetRealRoadGeometry(mwmId, featureId).IsOneWay(); + return GetRealRoadGeometry(mwmId, featureId, isOutgoing).IsOneWay(); } -bool TransitWorldGraph::IsPassThroughAllowed(NumMwmId mwmId, uint32_t featureId) +bool TransitWorldGraph::IsPassThroughAllowed(NumMwmId mwmId, uint32_t featureId, bool isOutgoing) { if (TransitGraph::IsTransitFeature(featureId)) return true; - return GetRealRoadGeometry(mwmId, featureId).IsPassThroughAllowed(); + return GetRealRoadGeometry(mwmId, featureId, isOutgoing).IsPassThroughAllowed(); } void TransitWorldGraph::ClearCachedGraphs() @@ -116,7 +121,7 @@ RouteWeight TransitWorldGraph::HeuristicCostEstimate(ms::LatLon const & from, ms return RouteWeight(m_estimator->CalcHeuristic(from, to)); } -RouteWeight TransitWorldGraph::CalcSegmentWeight(Segment const & segment, +RouteWeight TransitWorldGraph::CalcSegmentWeight(Segment const & segment, bool isOutgoing, EdgeEstimator::Purpose purpose) { if (TransitGraph::IsTransitSegment(segment)) @@ -126,7 +131,8 @@ RouteWeight TransitWorldGraph::CalcSegmentWeight(Segment const & segment, } return RouteWeight(m_estimator->CalcSegmentWeight( - segment, GetRealRoadGeometry(segment.GetMwmId(), segment.GetFeatureId()), purpose)); + segment, GetRealRoadGeometry(segment.GetMwmId(), segment.GetFeatureId(), isOutgoing), + purpose)); } RouteWeight TransitWorldGraph::CalcLeapWeight(ms::LatLon const & from, ms::LatLon const & to) const @@ -141,33 +147,32 @@ RouteWeight TransitWorldGraph::CalcOffroadWeight(ms::LatLon const & from, return RouteWeight(m_estimator->CalcOffroad(from, to, purpose)); } -double TransitWorldGraph::CalculateETA(Segment const & from, Segment const & to) +double TransitWorldGraph::CalculateETA(Segment const & from, Segment const & to, bool isOutgoing) { if (TransitGraph::IsTransitSegment(from)) - return CalcSegmentWeight(to, EdgeEstimator::Purpose::ETA).GetWeight(); + return CalcSegmentWeight(to, isOutgoing, EdgeEstimator::Purpose::ETA).GetWeight(); if (TransitGraph::IsTransitSegment(to)) - return CalcSegmentWeight(to, EdgeEstimator::Purpose::ETA).GetWeight(); + return CalcSegmentWeight(to, isOutgoing, EdgeEstimator::Purpose::ETA).GetWeight(); if (from.GetMwmId() != to.GetMwmId()) { return m_estimator->CalcSegmentWeight(to, GetRealRoadGeometry(to.GetMwmId(), to - .GetFeatureId()), EdgeEstimator::Purpose::ETA); + .GetFeatureId(), isOutgoing), EdgeEstimator::Purpose::ETA); } auto & indexGraph = m_indexLoader->GetIndexGraph(from.GetMwmId()); - return indexGraph - .CalculateEdgeWeight(EdgeEstimator::Purpose::ETA, true /* isOutgoing */, from, to) + return indexGraph.CalculateEdgeWeight(EdgeEstimator::Purpose::ETA, from, to, isOutgoing) .GetWeight(); } -double TransitWorldGraph::CalculateETAWithoutPenalty(Segment const & segment) +double TransitWorldGraph::CalculateETAWithoutPenalty(Segment const & segment, bool isOutgoing) { if (TransitGraph::IsTransitSegment(segment)) - return CalcSegmentWeight(segment, EdgeEstimator::Purpose::ETA).GetWeight(); + return CalcSegmentWeight(segment, isOutgoing, EdgeEstimator::Purpose::ETA).GetWeight(); return m_estimator->CalcSegmentWeight( - segment, GetRealRoadGeometry(segment.GetMwmId(), segment.GetFeatureId()), + segment, GetRealRoadGeometry(segment.GetMwmId(), segment.GetFeatureId(), isOutgoing), EdgeEstimator::Purpose::ETA); } @@ -214,10 +219,12 @@ void TransitWorldGraph::GetTwinsInner(Segment const & segment, bool isOutgoing, m_crossMwmGraph->GetTwins(segment, isOutgoing, twins); } -RoadGeometry const & TransitWorldGraph::GetRealRoadGeometry(NumMwmId mwmId, uint32_t featureId) +RoadGeometry const & TransitWorldGraph::GetRealRoadGeometry(NumMwmId mwmId, uint32_t featureId, + bool isOutgoing) { - CHECK(!TransitGraph::IsTransitFeature(featureId), ("GetRealRoadGeometry not designed for transit.")); - return m_indexLoader->GetGeometry(mwmId).GetRoad(featureId); + CHECK(!TransitGraph::IsTransitFeature(featureId), + ("GetRealRoadGeometry not designed for transit.")); + return m_indexLoader->GetGeometry(mwmId).GetRoad(featureId, isOutgoing); } void TransitWorldGraph::AddRealEdges(astar::VertexData const & vertexData, diff --git a/routing/transit_world_graph.hpp b/routing/transit_world_graph.hpp index fd7d1dd9824..6b86971a596 100644 --- a/routing/transit_world_graph.hpp +++ b/routing/transit_world_graph.hpp @@ -51,24 +51,26 @@ class TransitWorldGraph final : public WorldGraph return weight.GetWeight() - weight.GetTransitTime() <= MaxPedestrianTimeSec(startToFinishDistanceM); } - LatLonWithAltitude const & GetJunction(Segment const & segment, bool front) override; - ms::LatLon const & GetPoint(Segment const & segment, bool front) override; + LatLonWithAltitude const & GetJunction(Segment const & segment, bool front, + bool isOutgoing) override; + ms::LatLon const & GetPoint(Segment const & segment, bool front, bool isOutgoing) override; // All transit features are oneway. - bool IsOneWay(NumMwmId mwmId, uint32_t featureId) override; + bool IsOneWay(NumMwmId mwmId, uint32_t featureId, bool isOutgoing) override; // All transit features are allowed for through passage. - bool IsPassThroughAllowed(NumMwmId mwmId, uint32_t featureId) override; + bool IsPassThroughAllowed(NumMwmId mwmId, uint32_t featureId, bool isOutgoing) override; void ClearCachedGraphs() override; void SetMode(WorldGraphMode mode) override { m_mode = mode; } WorldGraphMode GetMode() const override { return m_mode; } RouteWeight HeuristicCostEstimate(ms::LatLon const & from, ms::LatLon const & to) override; - RouteWeight CalcSegmentWeight(Segment const & segment, EdgeEstimator::Purpose purpose) override; + RouteWeight CalcSegmentWeight(Segment const & segment, bool isOutgoing, + EdgeEstimator::Purpose purpose) override; RouteWeight CalcLeapWeight(ms::LatLon const & from, ms::LatLon const & to) const override; RouteWeight CalcOffroadWeight(ms::LatLon const & from, ms::LatLon const & to, EdgeEstimator::Purpose purpose) const override; - double CalculateETA(Segment const & from, Segment const & to) override; - double CalculateETAWithoutPenalty(Segment const & segment) override; + double CalculateETA(Segment const & from, Segment const & to, bool /* isOutgoing */) override; + double CalculateETAWithoutPenalty(Segment const & segment, bool /* isOutgoing */) override; std::unique_ptr GetTransitInfo(Segment const & segment) override; @@ -88,7 +90,7 @@ class TransitWorldGraph final : public WorldGraph return 50 * 60 + (startToFinishDistanceM / 1000) * 3 * 60; } - RoadGeometry const & GetRealRoadGeometry(NumMwmId mwmId, uint32_t featureId); + RoadGeometry const & GetRealRoadGeometry(NumMwmId mwmId, uint32_t featureId, bool isOutgoing); void AddRealEdges(astar::VertexData const & vertexData, bool isOutgoing, bool useRoutingOptions, std::vector & edges); TransitGraph & GetTransitGraph(NumMwmId mwmId); diff --git a/routing/world_graph.cpp b/routing/world_graph.cpp index 9bb48ec7131..6eae0778ff4 100644 --- a/routing/world_graph.cpp +++ b/routing/world_graph.cpp @@ -28,12 +28,12 @@ void WorldGraph::GetTwins(Segment const & segment, bool isOutgoing, bool useRout SetMode(prevMode); } -RoutingOptions WorldGraph::GetRoutingOptions(Segment const & /* segment */) +RoutingOptions WorldGraph::GetRoutingOptions(Segment const & /* segment */, bool /* isOutgoing */) { return {}; } -bool WorldGraph::IsRoutingOptionsGood(Segment const & /* segment */) +bool WorldGraph::IsRoutingOptionsGood(Segment const & /* segment */, bool /* isOutgoing */) { return true; } diff --git a/routing/world_graph.hpp b/routing/world_graph.hpp index ed1f827efb0..dd9628db4db 100644 --- a/routing/world_graph.hpp +++ b/routing/world_graph.hpp @@ -28,6 +28,20 @@ namespace routing { +/// \Note. About isOutgoing parameter. +/// In routing in hundreds of method isOutgoing boolean flag is used. This flag have +/// several meanings. +/// - Originally this parameter was added to distinguish getting ingoing graph edges and +/// outgoing graph edges. For example, if it's necessary to get outgoing edges +/// GetEdgeList(..., true /* isOutgoing */, ...) should be called and to get ingoing +/// graph edges GetEdgeList(..., false /* isOutgoing */, ...) should be called. +/// - On the other hand getting ingoing edges (isOutgoing == false) only for backward wave +/// in bidirectional A*. So it's possible to say that based on isOutgoing value +/// it's possible to say which wave in bidirectional A* is used. +/// - Then two-threads variant was implemented for bidirectional A*. A new thread is created +/// for backward A* bidirectional wave in this case. So if isOutgoing == false +/// that means the method is called from this additional thread. + enum class WorldGraphMode { LeapsOnly, // Mode for building a cross mwm route containing only leaps. In case of start and @@ -65,12 +79,12 @@ class WorldGraph // start to finish of the route. virtual bool CheckLength(RouteWeight const & weight, double startToFinishDistanceM) const = 0; - virtual LatLonWithAltitude const & GetJunction(Segment const & segment, bool front) = 0; - virtual ms::LatLon const & GetPoint(Segment const & segment, bool front) = 0; - virtual bool IsOneWay(NumMwmId mwmId, uint32_t featureId) = 0; + virtual LatLonWithAltitude const & GetJunction(Segment const & segment, bool front, bool isOutgoing) = 0; + virtual ms::LatLon const & GetPoint(Segment const & segment, bool front, bool isOutgoing) = 0; + virtual bool IsOneWay(NumMwmId mwmId, uint32_t featureId, bool isOutgoing) = 0; // Checks whether feature is allowed for through passage. - virtual bool IsPassThroughAllowed(NumMwmId mwmId, uint32_t featureId) = 0; + virtual bool IsPassThroughAllowed(NumMwmId mwmId, uint32_t featureId, bool isOutgoing) = 0; // Clear memory used by loaded graphs. virtual void ClearCachedGraphs() = 0; @@ -79,7 +93,7 @@ class WorldGraph virtual RouteWeight HeuristicCostEstimate(ms::LatLon const & from, ms::LatLon const & to) = 0; - virtual RouteWeight CalcSegmentWeight(Segment const & segment, + virtual RouteWeight CalcSegmentWeight(Segment const & segment, bool isOutgoing, EdgeEstimator::Purpose purpose) = 0; virtual RouteWeight CalcLeapWeight(ms::LatLon const & from, ms::LatLon const & to) const = 0; @@ -87,14 +101,14 @@ class WorldGraph virtual RouteWeight CalcOffroadWeight(ms::LatLon const & from, ms::LatLon const & to, EdgeEstimator::Purpose purpose) const = 0; - virtual double CalculateETA(Segment const & from, Segment const & to) = 0; - virtual double CalculateETAWithoutPenalty(Segment const & segment) = 0; + virtual double CalculateETA(Segment const & from, Segment const & to, bool /* isOutgoing */) = 0; + virtual double CalculateETAWithoutPenalty(Segment const & segment, bool /* isOutgoing */) = 0; /// \returns transitions for mwm with id |numMwmId|. virtual std::vector const & GetTransitions(NumMwmId numMwmId, bool isEnter); - virtual bool IsRoutingOptionsGood(Segment const & /* segment */); - virtual RoutingOptions GetRoutingOptions(Segment const & /* segment */); + virtual bool IsRoutingOptionsGood(Segment const & /* segment */, bool /* isOutgoing */); + virtual RoutingOptions GetRoutingOptions(Segment const & /* segment */, bool /* isOutgoing */); virtual void SetRoutingOptions(RoutingOptions /* routingOptions */); virtual void SetAStarParents(bool forward, Parents & parents); @@ -108,6 +122,8 @@ class WorldGraph Parents & backwardParents, std::function && fakeFeatureConverter); + virtual bool IsTwoThreadsReady() const { return false; } + /// \returns transit-specific information for segment. For nontransit segments returns nullptr. virtual std::unique_ptr GetTransitInfo(Segment const & segment) = 0; diff --git a/track_analyzing/track_analyzer/cmd_table.cpp b/track_analyzing/track_analyzer/cmd_table.cpp index 404989c412b..1f47d270730 100644 --- a/track_analyzing/track_analyzer/cmd_table.cpp +++ b/track_analyzing/track_analyzer/cmd_table.cpp @@ -407,7 +407,8 @@ void CmdTagsTable(string const & filepath, string const & trackExtension, String Geometry geometry(GeometryLoader::CreateFromFile(mwmFile, vehicleModel)); auto const vehicleType = VehicleType::Car; auto const edgeEstimator = EdgeEstimator::Create(vehicleType, *vehicleModel, nullptr /* trafficStash */); - auto indexGraphLoader = IndexGraphLoader::Create(vehicleType, false /* loadAltitudes */, numMwmIds, + auto indexGraphLoader = IndexGraphLoader::Create(vehicleType, false /* loadAltitudes */, + false /* twoThreadsReady */, numMwmIds, carModelFactory, edgeEstimator, dataSource); platform::CountryFile const countryFile(mwmName); diff --git a/track_analyzing/track_analyzer/cmd_tracks.cpp b/track_analyzing/track_analyzer/cmd_tracks.cpp index d06e9ce7db4..7c3ff9a1aa7 100644 --- a/track_analyzing/track_analyzer/cmd_tracks.cpp +++ b/track_analyzing/track_analyzer/cmd_tracks.cpp @@ -115,8 +115,9 @@ double EstimateDuration(MatchedTrack const & track, shared_ptr es continue; segment = point.GetSegment(); - result += estimator->CalcSegmentWeight(segment, geometry.GetRoad(segment.GetFeatureId()), - EdgeEstimator::Purpose::ETA); + result += estimator->CalcSegmentWeight( + segment, geometry.GetRoad(segment.GetFeatureId(), true /* isOutgoing */), + EdgeEstimator::Purpose::ETA); } return result; diff --git a/track_analyzing/track_analyzer/crossroad_checker.cpp b/track_analyzing/track_analyzer/crossroad_checker.cpp index 3aa17c7d5fe..4e9e0226b07 100644 --- a/track_analyzing/track_analyzer/crossroad_checker.cpp +++ b/track_analyzing/track_analyzer/crossroad_checker.cpp @@ -78,9 +78,11 @@ namespace routing IsCrossroadChecker::Type IsCrossroadChecker::operator()(Segment const & current, Segment const & next) const { auto const currentSegmentFeatureId = current.GetFeatureId(); - auto const currentSegmentHwType = m_geometry.GetRoad(currentSegmentFeatureId).GetHighwayType(); + auto const currentSegmentHwType = + m_geometry.GetRoad(currentSegmentFeatureId, true /* isOutgoing */).GetHighwayType(); auto const nextSegmentFeatureId = next.GetFeatureId(); - auto const nextSegmentHwType = m_geometry.GetRoad(nextSegmentFeatureId).GetHighwayType(); + auto const nextSegmentHwType = + m_geometry.GetRoad(nextSegmentFeatureId, true /* isOutgoing */).GetHighwayType(); auto const currentRoadPoint = current.GetRoadPoint(true /* isFront */); auto const jointId = m_indexGraph.GetJointId(currentRoadPoint); if (jointId == Joint::kInvalidId) @@ -121,7 +123,7 @@ IsCrossroadChecker::Type IsCrossroadChecker::operator()(Segment const & current, if (pointFeatureId == currentSegmentFeatureId || pointFeatureId == nextSegmentFeatureId) return; - auto const & roadGeometry = m_geometry.GetRoad(pointFeatureId); + auto const & roadGeometry = m_geometry.GetRoad(pointFeatureId, true /* isOutgoing */); auto const pointHwType = roadGeometry.GetHighwayType(); if (currentSegmentHwType == pointHwType) return; diff --git a/track_analyzing/track_matcher.cpp b/track_analyzing/track_matcher.cpp index ef21b6dbd84..dc4bca322bb 100644 --- a/track_analyzing/track_matcher.cpp +++ b/track_analyzing/track_matcher.cpp @@ -34,9 +34,11 @@ double DistanceToSegment(m2::PointD const & segmentBegin, m2::PointD const & seg double DistanceToSegment(Segment const & segment, m2::PointD const & point, IndexGraph & indexGraph) { - return DistanceToSegment( - mercator::FromLatLon(indexGraph.GetGeometry().GetPoint(segment.GetRoadPoint(false))), - mercator::FromLatLon(indexGraph.GetGeometry().GetPoint(segment.GetRoadPoint(true))), point); + return DistanceToSegment(mercator::FromLatLon(indexGraph.GetGeometry().GetPoint( + segment.GetRoadPoint(false /* front */), true /* isOutgoing */)), + mercator::FromLatLon(indexGraph.GetGeometry().GetPoint( + segment.GetRoadPoint(true /* front */), true /* isOutgoing */)), + point); } bool EdgesContain(vector const & edges, Segment const & segment) @@ -69,7 +71,7 @@ TrackMatcher::TrackMatcher(storage::Storage const & storage, NumMwmId mwmId, m_graph = make_unique( make_shared(GeometryLoader::Create(m_dataSource, handle, m_vehicleModel, AttrLoader(m_dataSource, handle), - false /* loadAltitudes */)), + false /* loadAltitudes */, false /* twoThreadsReady */)), EdgeEstimator::Create(VehicleType::Car, *m_vehicleModel, nullptr /* trafficStash */)); DeserializeIndexGraph(*handle.GetValue(), VehicleType::Car, *m_graph); diff --git a/track_analyzing/utils.cpp b/track_analyzing/utils.cpp index cb927731cf0..307e50dd0e9 100644 --- a/track_analyzing/utils.cpp +++ b/track_analyzing/utils.cpp @@ -31,8 +31,8 @@ double CalcSubtrackLength(MatchedTrack::const_iterator begin, MatchedTrack::cons { length += ms::DistanceOnEarth( - geometry.GetPoint(segment.GetRoadPoint(false /* front */)), - geometry.GetPoint(segment.GetRoadPoint(true /* front */))); + geometry.GetPoint(segment.GetRoadPoint(false /* front */), true /* isOutgoing */), + geometry.GetPoint(segment.GetRoadPoint(true /* front */), true /* isOutgoing */)); prevSegment = segment; } }