From 0fe371987869604d0d0aca06f772354b4d1f0d6b Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Tue, 27 Oct 2020 15:01:32 +0300 Subject: [PATCH 01/39] [routing] Adding isOutgoing param to methods to be able to use different caches from differnt threads. --- generator/restriction_collector.cpp | 10 +- generator/routing_index_generator.cpp | 9 +- routing/base/astar_algorithm.hpp | 28 +++--- routing/base/astar_graph.hpp | 2 +- routing/fake_ending.cpp | 8 +- routing/geometry.cpp | 2 +- routing/geometry.hpp | 6 +- routing/index_graph.cpp | 35 +++---- routing/index_graph.hpp | 14 +-- routing/index_graph_starter.cpp | 97 ++++++++++--------- routing/index_graph_starter.hpp | 37 +++---- routing/index_graph_starter_joints.hpp | 25 ++--- routing/index_router.cpp | 24 ++--- routing/junction_visitor.hpp | 7 +- routing/leaps_graph.cpp | 31 +++--- routing/leaps_graph.hpp | 10 +- routing/leaps_postprocessor.cpp | 9 +- routing/restriction_loader.cpp | 3 +- routing/routing_helpers.cpp | 8 +- routing/routing_helpers.hpp | 4 +- routing/routing_tests/index_graph_test.cpp | 3 +- routing/routing_tests/index_graph_tools.cpp | 3 +- routing/routing_tests/index_graph_tools.hpp | 6 +- routing/routing_tests/routing_algorithm.cpp | 4 +- routing/routing_tests/routing_algorithm.hpp | 2 +- routing/single_vehicle_world_graph.cpp | 48 ++++----- routing/single_vehicle_world_graph.hpp | 22 +++-- routing/transit_world_graph.cpp | 59 ++++++----- routing/transit_world_graph.hpp | 18 ++-- routing/world_graph.cpp | 4 +- routing/world_graph.hpp | 18 ++-- track_analyzing/track_analyzer/cmd_tracks.cpp | 5 +- .../track_analyzer/crossroad_checker.cpp | 8 +- track_analyzing/track_matcher.cpp | 8 +- track_analyzing/utils.cpp | 4 +- 35 files changed, 315 insertions(+), 266 deletions(-) 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..2b2891665a0 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(); @@ -202,9 +202,9 @@ class IndexGraphWrapper final routing::RouteWeight GetAStarWeightEpsilon() { return routing::RouteWeight(0.0); } // @} - 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, @@ -256,7 +256,8 @@ class DijkstraWrapperJoints : public routing::IndexGraphStarterJoints(); } diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index 4ea7fa74633..65222417b65 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -303,8 +303,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); + // TODO. The last parameter may be false in case of two thread routing. + auto const piF = graph.HeuristicCostEstimate(v, finalVertex, true /* forward */); + auto const piR = graph.HeuristicCostEstimate(v, startVertex, true /* forward */); if (forward) { /// @todo careful: with this "return" here and below in the Backward case @@ -482,8 +483,8 @@ AStarAlgorithm::FindPath(P & params, RoutingResult::FindPath(P & params, RoutingResult::FindPath(P & params, RoutingResult::FindPath(P & params, RoutingResult::FindPathBidirectional(P & params, BidirectionalStepContext * nxt = &backward; auto const getResult = [&]() { - if (!params.m_checkLengthCallback(bestPathRealLength)) + // TODO. |forward| may be false or true in case of two thread routing. + if (!params.m_checkLengthCallback(bestPathRealLength, true /* forward */)) return Result::NoPath; ReconstructPathBidirectional(cur->bestVertex, nxt->bestVertex, cur->parent, nxt->parent, @@ -638,8 +640,9 @@ AStarAlgorithm::FindPathBidirectional(P & params, if (cur->ExistsStateWithBetterDistance(stateV)) continue; + // TODO. |forward| may be false or true in case of two thread routing. params.m_onVisitedVertexCallback(stateV.vertex, - cur->forward ? cur->finalVertex : cur->startVertex); + cur->forward ? cur->finalVertex : cur->startVertex, true /* forward */); cur->GetAdjacencyList(stateV, adj); auto const & pV = stateV.heuristic; @@ -660,7 +663,8 @@ 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)) + // TODO. |forward| may be false or true in case of two thread routing. + if (!params.m_checkLengthCallback(fullLength, true /* forward */)) continue; if (cur->ExistsStateWithBetterDistance(stateW, epsilon)) @@ -746,7 +750,7 @@ AStarAlgorithm::AdjustRoute(P & params, return false; } - params.m_onVisitedVertexCallback(startVertex, vertex); + params.m_onVisitedVertexCallback(startVertex, vertex, true /* forward */); auto it = remainingDistances.find(vertex); if (it != remainingDistances.cend()) @@ -767,7 +771,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; }; diff --git a/routing/base/astar_graph.hpp b/routing/base/astar_graph.hpp index 37e95e34665..0e5f815b61d 100644 --- a/routing/base/astar_graph.hpp +++ b/routing/base/astar_graph.hpp @@ -20,7 +20,7 @@ class AStarGraph using Parents = ska::bytell_hash_map; - virtual Weight HeuristicCostEstimate(Vertex const & from, Vertex const & to) = 0; + virtual Weight HeuristicCostEstimate(Vertex const & from, Vertex const & to, bool isOutgoing) = 0; virtual void GetOutgoingEdgesList(astar::VertexData const & vertexData, std::vector & edges) = 0; 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/geometry.cpp b/routing/geometry.cpp index 98aa30e2ff0..5e46e7bf8ae 100644 --- a/routing/geometry.cpp +++ b/routing/geometry.cpp @@ -262,7 +262,7 @@ Geometry::Geometry(unique_ptr loader) 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, ()); diff --git a/routing/geometry.hpp b/routing/geometry.hpp index 1d60cc4f3c4..baf3b126711 100644 --- a/routing/geometry.hpp +++ b/routing/geometry.hpp @@ -139,13 +139,13 @@ class Geometry final /// \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: diff --git a/routing/index_graph.cpp b/routing/index_graph.cpp index d168dbda3a8..bd4d205720a 100644 --- a/routing/index_graph.cpp +++ b/routing/index_graph.cpp @@ -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; @@ -399,8 +399,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 +457,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 +537,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 +556,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..b31b5fdb21b 100644 --- a/routing/index_graph.hpp +++ b/routing/index_graph.hpp @@ -113,9 +113,11 @@ class IndexGraph final 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,8 @@ 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, + RouteWeight CalculateEdgeWeight(EdgeEstimator::Purpose purpose, + Segment const & from, Segment const & to, bool isOutgoing, std::optional const & prevWeight = std::nullopt); template @@ -164,14 +166,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_starter.cpp b/routing/index_graph_starter.cpp index ccc13413d57..74da9f03818 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) @@ -540,31 +544,32 @@ void IndexGraphStarter::AddFakeEdges(Segment const & segment, bool isOutgoing, v { // 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..b18ba8d7792 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,7 @@ class IndexGraphStarter : public AStarGraph const & parentVertexData, Segment const & segment, bool isOutgoing, std::vector & edges, @@ -119,10 +119,10 @@ class IndexGraphStarter : public AStarGraph & parents) override @@ -149,16 +149,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..33c9e4f5961 100644 --- a/routing/index_graph_starter_joints.hpp +++ b/routing/index_graph_starter_joints.hpp @@ -40,13 +40,13 @@ class IndexGraphStarterJoints : public AStarGraph const & vertexData, std::vector & edges) override @@ -259,7 +259,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)) @@ -287,7 +287,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; @@ -303,18 +304,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 @@ -617,11 +618,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); }; diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 11fc901e587..16db895be75 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -718,7 +718,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)); @@ -922,7 +922,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; @@ -1331,8 +1331,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 +1379,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 +1410,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 +1474,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 +1486,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 +1513,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/junction_visitor.hpp b/routing/junction_visitor.hpp index 9e09efcb443..afa84fb55e3 100644 --- a/routing/junction_visitor.hpp +++ b/routing/junction_visitor.hpp @@ -27,20 +27,21 @@ class JunctionVisitor m_lastProgressPercent = progress->GetLastPercent(); } - void operator()(Vertex const & from, Vertex const & to) + void operator()(Vertex const & from, Vertex const & to, bool isOutgoing) { + // @TODO This method may call from different threads. All the data should be protected. ++m_visitCounter; if (m_visitCounter % m_visitPeriod != 0) return; - auto const & pointFrom = m_graph.GetPoint(from, true /* front */); + auto const & pointFrom = m_graph.GetPoint(from, true /* front */, isOutgoing); m_delegate.OnPointCheck(pointFrom); auto progress = m_progress.lock(); if (!progress) return; - auto const & pointTo = m_graph.GetPoint(to, true /* front */); + auto const & pointTo = m_graph.GetPoint(to, true /* front */, isOutgoing); auto const currentPercent = progress->UpdateProgress(pointFrom, pointTo); if (currentPercent - m_lastProgressPercent > kProgressInterval) { 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..56771cdafaa 100644 --- a/routing/restriction_loader.cpp +++ b/routing/restriction_loader.cpp @@ -162,7 +162,8 @@ void ConvertRestrictionsOnlyUTurnToNo(IndexGraph & graph, if (!graph.IsRoad(featureId)) continue; - uint32_t const n = graph.GetGeometry().GetRoad(featureId).GetPointsCount(); + // @TODO isOutgoing should be passed from IndexGraphLoaderImpl::GetIndexGraph() + 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/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_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..6f808e67a39 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -91,7 +91,8 @@ void NoUTurnRestrictionTest::TestRouteGeom(Segment const & start, Segment const 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], diff --git a/routing/routing_tests/index_graph_tools.hpp b/routing/routing_tests/index_graph_tools.hpp index adba903d74d..817ca1192f4 100644 --- a/routing/routing_tests/index_graph_tools.hpp +++ b/routing/routing_tests/index_graph_tools.hpp @@ -55,10 +55,10 @@ class WorldGraphForAStar : public AStarGraph // 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, diff --git a/routing/routing_tests/routing_algorithm.cpp b/routing/routing_tests/routing_algorithm.cpp index 5f023a6a3c6..5f69d1bf26b 100644 --- a/routing/routing_tests/routing_algorithm.cpp +++ b/routing/routing_tests/routing_algorithm.cpp @@ -45,7 +45,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; } @@ -157,7 +157,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..3b0fe463abe 100644 --- a/routing/routing_tests/routing_algorithm.hpp +++ b/routing/routing_tests/routing_algorithm.hpp @@ -39,7 +39,7 @@ 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; // @} void GetEdgesList(Vertex const & vertex, bool /* isOutgoing */, std::vector & adj); diff --git a/routing/single_vehicle_world_graph.cpp b/routing/single_vehicle_world_graph.cpp index eb40b9fccc5..4921e8b78eb 100644 --- a/routing/single_vehicle_world_graph.cpp +++ b/routing/single_vehicle_world_graph.cpp @@ -125,25 +125,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 +154,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 +174,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 +204,9 @@ 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 +215,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..585aeb63a55 100644 --- a/routing/single_vehicle_world_graph.hpp +++ b/routing/single_vehicle_world_graph.hpp @@ -50,11 +50,12 @@ 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; } @@ -62,19 +63,20 @@ class SingleVehicleWorldGraph final : public WorldGraph 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; @@ -124,7 +126,7 @@ 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; 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..f3a136614a6 100644 --- a/routing/world_graph.hpp +++ b/routing/world_graph.hpp @@ -65,12 +65,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 +79,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 +87,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); 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..52d803133fd 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) 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; } } From 5086915061bbf985ce457e2923c526c7cccced82 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Tue, 27 Oct 2020 16:51:56 +0300 Subject: [PATCH 02/39] Passing isOutgoing build fix. --- generator/routing_index_generator.cpp | 8 ++++---- routing/base/astar_algorithm.hpp | 6 +++--- routing/index_graph.cpp | 2 +- routing/index_graph.hpp | 2 +- routing/index_graph_starter.cpp | 8 ++++---- routing/index_graph_starter.hpp | 6 +++--- routing/index_graph_starter_joints.hpp | 16 ++++++++-------- routing/index_road_graph.cpp | 10 ++++++---- routing/routing_tests/astar_algorithm_test.cpp | 8 ++++---- routing/routing_tests/index_graph_tools.cpp | 8 ++++---- 10 files changed, 38 insertions(+), 36 deletions(-) diff --git a/generator/routing_index_generator.cpp b/generator/routing_index_generator.cpp index 2b2891665a0..e8302322a67 100644 --- a/generator/routing_index_generator.cpp +++ b/generator/routing_index_generator.cpp @@ -223,14 +223,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 diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index 65222417b65..b8b693ed0b9 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -28,13 +28,13 @@ namespace astar template struct DefaultVisitor { - void operator()(Vertex const & /* from */, Vertex const & /* to */) const {}; + void operator()(Vertex const & /* from */, Vertex const & /* to */, bool /* isOutgoing */) const {}; }; template struct DefaultLengthChecker { - bool operator()(Weight const & /* weight */) const { return true; } + bool operator()(Weight const & /* weight */, bool /* isOutgoing */) const { return true; } }; } // namespace astar @@ -540,7 +540,7 @@ AStarAlgorithm::FindPath(P & params, RoutingResult const & children, bool isOutgoing, std::vector & lastPoints); diff --git a/routing/index_graph_starter.cpp b/routing/index_graph_starter.cpp index 74da9f03818..1482b8a3ae0 100644 --- a/routing/index_graph_starter.cpp +++ b/routing/index_graph_starter.cpp @@ -532,13 +532,13 @@ 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)) { diff --git a/routing/index_graph_starter.hpp b/routing/index_graph_starter.hpp index b18ba8d7792..08373d47d05 100644 --- a/routing/index_graph_starter.hpp +++ b/routing/index_graph_starter.hpp @@ -176,14 +176,14 @@ class IndexGraphStarter : public AStarGraph const & vertexData, @@ -163,7 +163,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); @@ -275,7 +275,7 @@ void IndexGraphStarterJoints::InitEnding(Segment const & ending, bool sta 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) { @@ -569,7 +569,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; @@ -636,7 +636,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/routing_tests/astar_algorithm_test.cpp b/routing/routing_tests/astar_algorithm_test.cpp index 0c9ed4edeaa..33039b22b16 100644 --- a/routing/routing_tests/astar_algorithm_test.cpp +++ b/routing/routing_tests/astar_algorithm_test.cpp @@ -63,7 +63,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 +94,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 +118,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 +141,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/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index 6f808e67a39..b2623cc917c 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -519,13 +519,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) { @@ -583,8 +583,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); } From e9c2ea525e633ad09f57dbe6089d6f79fbb27a0d Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Wed, 28 Oct 2020 17:39:12 +0300 Subject: [PATCH 03/39] [routing] Adding AStarGraph::m_multithreadingReady. --- routing/base/astar_graph.hpp | 11 ++++++++++- routing/routing_tests/index_graph_tools.cpp | 1 - routing/routing_tests/index_graph_tools.hpp | 8 +++++++- routing/routing_tests/routing_algorithm.cpp | 9 ++++++++- routing/routing_tests/routing_algorithm.hpp | 2 ++ 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/routing/base/astar_graph.hpp b/routing/base/astar_graph.hpp index 0e5f815b61d..f5b6dee3c9c 100644 --- a/routing/base/astar_graph.hpp +++ b/routing/base/astar_graph.hpp @@ -20,6 +20,12 @@ class AStarGraph using Parents = ska::bytell_hash_map; + constexpr AStarGraph(bool multithreadingReady = false) + : m_multithreadingReady(multithreadingReady) + { + } + virtual ~AStarGraph() = default; + virtual Weight HeuristicCostEstimate(Vertex const & from, Vertex const & to, bool isOutgoing) = 0; virtual void GetOutgoingEdgesList(astar::VertexData const & vertexData, @@ -34,7 +40,10 @@ class AStarGraph virtual Weight GetAStarWeightEpsilon(); - virtual ~AStarGraph() = default; + constexpr bool GetMultithreadingReady() const { return m_multithreadingReady;} + +private: + bool const m_multithreadingReady; }; template diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index b2623cc917c..244161e1253 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 diff --git a/routing/routing_tests/index_graph_tools.hpp b/routing/routing_tests/index_graph_tools.hpp index 817ca1192f4..6bbcbacbd2d 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,7 +52,11 @@ 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(!GetMultithreadingReady(), ()); + } ~WorldGraphForAStar() override = default; // AStarGraph overrides: diff --git a/routing/routing_tests/routing_algorithm.cpp b/routing/routing_tests/routing_algorithm.cpp index 5f69d1bf26b..546b4e9f38c 100644 --- a/routing/routing_tests/routing_algorithm.cpp +++ b/routing/routing_tests/routing_algorithm.cpp @@ -16,6 +16,11 @@ namespace routing_test { +UndirectedGraph::UndirectedGraph() +{ + CHECK(!GetMultithreadingReady(), ()); +} + void UndirectedGraph::AddEdge(Vertex u, Vertex v, Weight w) { m_adjs[u].emplace_back(v, w); @@ -115,7 +120,9 @@ class RoadGraph : public Algorithm::Graph explicit RoadGraph(IRoadGraph const & roadGraph) : m_roadGraph(roadGraph), m_maxSpeedMPS(KMPH2MPS(roadGraph.GetMaxSpeedKMpH())) - {} + { + CHECK(!GetMultithreadingReady(), ()); + } void GetOutgoingEdgesList(astar::VertexData const & vertexData, std::vector & adj) override diff --git a/routing/routing_tests/routing_algorithm.hpp b/routing/routing_tests/routing_algorithm.hpp index 3b0fe463abe..1cb53a89a13 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; From 61c4a1d74f89ec45db77891cb2465eea94316603 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Thu, 29 Oct 2020 08:05:28 +0300 Subject: [PATCH 04/39] [routing] Adding comment to explain why isOutgoing should be hardcode in ConvertRestrictionsOnlyUTurnToNo(). --- routing/restriction_loader.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/routing/restriction_loader.cpp b/routing/restriction_loader.cpp index 56771cdafaa..186a08194ef 100644 --- a/routing/restriction_loader.cpp +++ b/routing/restriction_loader.cpp @@ -162,7 +162,15 @@ void ConvertRestrictionsOnlyUTurnToNo(IndexGraph & graph, if (!graph.IsRoad(featureId)) continue; - // @TODO isOutgoing should be passed from IndexGraphLoaderImpl::GetIndexGraph() + // 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) From d02e6c11f5b63fb609404200bdc34fdc6988efd3 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Thu, 29 Oct 2020 16:09:44 +0300 Subject: [PATCH 05/39] [routing] Adding syncronization staff to IndexGraphStarterJoints. --- routing/base/astar_graph.hpp | 6 +-- routing/index_graph_starter.cpp | 2 +- routing/index_graph_starter.hpp | 3 +- routing/index_graph_starter_joints.hpp | 59 ++++++++++++++++----- routing/index_router.cpp | 8 +-- routing/routing_tests/index_graph_tools.cpp | 2 +- routing/routing_tests/index_graph_tools.hpp | 2 +- routing/routing_tests/routing_algorithm.cpp | 4 +- 8 files changed, 62 insertions(+), 24 deletions(-) diff --git a/routing/base/astar_graph.hpp b/routing/base/astar_graph.hpp index f5b6dee3c9c..99a32f4f8b1 100644 --- a/routing/base/astar_graph.hpp +++ b/routing/base/astar_graph.hpp @@ -21,7 +21,7 @@ class AStarGraph using Parents = ska::bytell_hash_map; constexpr AStarGraph(bool multithreadingReady = false) - : m_multithreadingReady(multithreadingReady) + : m_twoThreadsReady(multithreadingReady) { } virtual ~AStarGraph() = default; @@ -40,10 +40,10 @@ class AStarGraph virtual Weight GetAStarWeightEpsilon(); - constexpr bool GetMultithreadingReady() const { return m_multithreadingReady;} + constexpr bool IsTwoThreadsReady() const { return m_twoThreadsReady;} private: - bool const m_multithreadingReady; + bool const m_twoThreadsReady; }; template diff --git a/routing/index_graph_starter.cpp b/routing/index_graph_starter.cpp index 1482b8a3ae0..554786180d3 100644 --- a/routing/index_graph_starter.cpp +++ b/routing/index_graph_starter.cpp @@ -58,7 +58,7 @@ size_t IndexGraphStarter::GetRouteNumPoints(vector const & segments) IndexGraphStarter::IndexGraphStarter(FakeEnding const & startEnding, FakeEnding const & finishEnding, uint32_t fakeNumerationStart, - bool strictForward, WorldGraph & graph) + bool strictForward, bool twoThreadsReady, WorldGraph & graph) : m_graph(graph) { m_fakeNumerationStart = fakeNumerationStart; diff --git a/routing/index_graph_starter.hpp b/routing/index_graph_starter.hpp index 08373d47d05..cbeeb40ec3c 100644 --- a/routing/index_graph_starter.hpp +++ b/routing/index_graph_starter.hpp @@ -57,7 +57,8 @@ class IndexGraphStarter : public AStarGraph #include #include +#include #include #include #include @@ -30,10 +32,18 @@ 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* nd two thread A*. + /// To create an instance of the class with synchronization staff |twoThreadsReady| + /// Should be set to true. It means the class is ready for calls HeuristicCostEstimate(), + /// GetOutgoingEdgesList() and GetIngoingEdgesList() from two different threads. + explicit IndexGraphStarterJoints(Graph & graph) + : m_graph(graph), m_reconstructedFakeJointsMtx(std::nullopt) + { + } IndexGraphStarterJoints(Graph & graph, Segment const & startSegment, - Segment const & endSegment); + Segment const & endSegment, + bool twoThreadsReady); IndexGraphStarterJoints(Graph & graph, Segment const & startSegment); @@ -184,6 +194,9 @@ class IndexGraphStarterJoints : public AStarGraph m_savedWeight; // JointSegment consists of two segments of one feature. @@ -209,8 +226,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,10 +244,13 @@ class IndexGraphStarterJoints : public AStarGraph -IndexGraphStarterJoints::IndexGraphStarterJoints(Graph & graph, - Segment const & startSegment, - Segment const & endSegment) - : m_graph(graph), m_startSegment(startSegment), m_endSegment(endSegment) +IndexGraphStarterJoints::IndexGraphStarterJoints(Graph & graph, Segment const & startSegment, + Segment const & endSegment, + bool twoThreadsReady) + : m_graph(graph) + , m_startSegment(startSegment) + , m_endSegment(endSegment) + , m_reconstructedFakeJointsMtx(twoThreadsReady ? std::optional() : std::nullopt) { Init(m_startSegment, m_endSegment); } @@ -232,7 +258,7 @@ IndexGraphStarterJoints::IndexGraphStarterJoints(Graph & graph, template IndexGraphStarterJoints::IndexGraphStarterJoints(Graph & graph, Segment const & startSegment) - : m_graph(graph), m_startSegment(startSegment) + : m_graph(graph), m_startSegment(startSegment), m_reconstructedFakeJointsMtx(std::nullopt) { InitEnding(startSegment, true /* start */); @@ -272,7 +298,10 @@ 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, true /* isOutgoing */); @@ -296,6 +325,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(); } @@ -329,11 +359,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(); @@ -610,6 +644,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)); }; diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 16db895be75..55055244978 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -653,7 +653,8 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, uint32_t const fakeNumerationStart = starter ? starter->GetNumFakeSegments() + startIdx : startIdx; IndexGraphStarter subrouteStarter(startFakeEnding, finishFakeEnding, fakeNumerationStart, - isStartSegmentStrictForward, *graph); + isStartSegmentStrictForward, false /* twoThreadsReady */, + *graph); if (m_guides.IsAttached()) { @@ -785,7 +786,8 @@ RouterResultCode IndexRouter::CalculateSubrouteJointsMode( shared_ptr const & progress, vector & subroute) { using JointsStarter = IndexGraphStarterJoints; - JointsStarter jointStarter(starter, starter.GetStartSegment(), starter.GetFinishSegment()); + JointsStarter jointStarter(starter, starter.GetStartSegment(), starter.GetFinishSegment(), + starter.IsTwoThreadsReady()); using Visitor = JunctionVisitor; Visitor visitor(jointStarter, delegate, kVisitPeriod, progress); @@ -912,7 +914,7 @@ RouterResultCode IndexRouter::AdjustRoute(Checkpoints const & checkpoints, FakeEnding dummy{}; IndexGraphStarter starter(MakeFakeEnding(startSegments, pointFrom, *graph), dummy, m_lastFakeEdges->GetNumFakeEdges(), bestSegmentIsAlmostCodirectional, - *graph); + false /* twoThreadsReady */, *graph); starter.Append(*m_lastFakeEdges); diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index 244161e1253..cd7dc4ece54 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -620,7 +620,7 @@ unique_ptr MakeStarter(FakeEnding const & start, FakeEnding c WorldGraph & graph) { return make_unique(start, finish, 0 /* fakeNumerationStart */, - false /* strictForward */, graph); + false /* strictForward */, false /* twoThreadsReady */, graph); } time_t GetUnixtimeByDate(uint16_t year, Month month, uint8_t monthDay, uint8_t hours, diff --git a/routing/routing_tests/index_graph_tools.hpp b/routing/routing_tests/index_graph_tools.hpp index 6bbcbacbd2d..5040823739b 100644 --- a/routing/routing_tests/index_graph_tools.hpp +++ b/routing/routing_tests/index_graph_tools.hpp @@ -55,7 +55,7 @@ class WorldGraphForAStar : public AStarGraph explicit WorldGraphForAStar(std::unique_ptr graph) : m_graph(std::move(graph)) { - CHECK(!GetMultithreadingReady(), ()); + CHECK(!IsTwoThreadsReady(), ()); } ~WorldGraphForAStar() override = default; diff --git a/routing/routing_tests/routing_algorithm.cpp b/routing/routing_tests/routing_algorithm.cpp index 546b4e9f38c..fdca5decc10 100644 --- a/routing/routing_tests/routing_algorithm.cpp +++ b/routing/routing_tests/routing_algorithm.cpp @@ -18,7 +18,7 @@ namespace routing_test { UndirectedGraph::UndirectedGraph() { - CHECK(!GetMultithreadingReady(), ()); + CHECK(!IsTwoThreadsReady(), ()); } void UndirectedGraph::AddEdge(Vertex u, Vertex v, Weight w) @@ -121,7 +121,7 @@ class RoadGraph : public Algorithm::Graph explicit RoadGraph(IRoadGraph const & roadGraph) : m_roadGraph(roadGraph), m_maxSpeedMPS(KMPH2MPS(roadGraph.GetMaxSpeedKMpH())) { - CHECK(!GetMultithreadingReady(), ()); + CHECK(!IsTwoThreadsReady(), ()); } void GetOutgoingEdgesList(astar::VertexData const & vertexData, From ed25d1985b1d738ce309c0b635065aa425e83974 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Mon, 2 Nov 2020 17:58:08 +0300 Subject: [PATCH 06/39] Fixing comments. --- routing/index_graph_starter_joints.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing/index_graph_starter_joints.hpp b/routing/index_graph_starter_joints.hpp index df82aa5b0a9..0daff71374a 100644 --- a/routing/index_graph_starter_joints.hpp +++ b/routing/index_graph_starter_joints.hpp @@ -32,9 +32,9 @@ template class IndexGraphStarterJoints : public AStarGraph { public: - /// \note This class may be used in two modes. For one thread A* nd two thread A*. + /// \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 |twoThreadsReady| - /// Should be set to true. It means the class is ready for calls HeuristicCostEstimate(), + /// should be set to true. It means the class is ready for calls HeuristicCostEstimate(), /// GetOutgoingEdgesList() and GetIngoingEdgesList() from two different threads. explicit IndexGraphStarterJoints(Graph & graph) : m_graph(graph), m_reconstructedFakeJointsMtx(std::nullopt) From c917672f0df08565e6605ea50ff2f51467a7cd1e Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Mon, 2 Nov 2020 20:04:24 +0300 Subject: [PATCH 07/39] Removing AStarGraph::m_twoThreadsReady to prevent duplicating states. --- generator/routing_index_generator.cpp | 1 + routing/base/astar_graph.hpp | 10 ++----- routing/index_graph_starter_joints.hpp | 40 +++++++++++++++++--------- routing/index_router.cpp | 3 +- 4 files changed, 30 insertions(+), 24 deletions(-) diff --git a/generator/routing_index_generator.cpp b/generator/routing_index_generator.cpp index e8302322a67..49eb156cf85 100644 --- a/generator/routing_index_generator.cpp +++ b/generator/routing_index_generator.cpp @@ -200,6 +200,7 @@ 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, bool isOutgoing) diff --git a/routing/base/astar_graph.hpp b/routing/base/astar_graph.hpp index 99a32f4f8b1..b4df7f66251 100644 --- a/routing/base/astar_graph.hpp +++ b/routing/base/astar_graph.hpp @@ -20,10 +20,7 @@ class AStarGraph using Parents = ska::bytell_hash_map; - constexpr AStarGraph(bool multithreadingReady = false) - : m_twoThreadsReady(multithreadingReady) - { - } + constexpr AStarGraph() {} virtual ~AStarGraph() = default; virtual Weight HeuristicCostEstimate(Vertex const & from, Vertex const & to, bool isOutgoing) = 0; @@ -40,10 +37,7 @@ class AStarGraph virtual Weight GetAStarWeightEpsilon(); - constexpr bool IsTwoThreadsReady() const { return m_twoThreadsReady;} - -private: - bool const m_twoThreadsReady; + virtual bool IsTwoThreadsReady() const { return false; } }; template diff --git a/routing/index_graph_starter_joints.hpp b/routing/index_graph_starter_joints.hpp index 0daff71374a..d355db67332 100644 --- a/routing/index_graph_starter_joints.hpp +++ b/routing/index_graph_starter_joints.hpp @@ -33,17 +33,14 @@ class IndexGraphStarterJoints : public AStarGraph +std::optional GetOptionalMutex(Graph const & graph) +{ + return graph.IsTwoThreadsReady() ? std::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, - bool twoThreadsReady) + Segment const & endSegment) : m_graph(graph) , m_startSegment(startSegment) , m_endSegment(endSegment) - , m_reconstructedFakeJointsMtx(twoThreadsReady ? std::optional() : std::nullopt) + , m_reconstructedFakeJointsMtx(GetOptionalMutex(graph)) { Init(m_startSegment, m_endSegment); } template -IndexGraphStarterJoints::IndexGraphStarterJoints(Graph & graph, - Segment const & startSegment) - : m_graph(graph), m_startSegment(startSegment), m_reconstructedFakeJointsMtx(std::nullopt) +IndexGraphStarterJoints::IndexGraphStarterJoints(Graph & graph, Segment const & startSegment) + : m_graph(graph) + , m_startSegment(startSegment) + , m_reconstructedFakeJointsMtx(GetOptionalMutex(graph)) { InitEnding(startSegment, true /* start */); diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 55055244978..48ee01177d9 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -786,8 +786,7 @@ RouterResultCode IndexRouter::CalculateSubrouteJointsMode( shared_ptr const & progress, vector & subroute) { using JointsStarter = IndexGraphStarterJoints; - JointsStarter jointStarter(starter, starter.GetStartSegment(), starter.GetFinishSegment(), - starter.IsTwoThreadsReady()); + JointsStarter jointStarter(starter, starter.GetStartSegment(), starter.GetFinishSegment()); using Visitor = JunctionVisitor; Visitor visitor(jointStarter, delegate, kVisitPeriod, progress); From d7d8977ba01bb580711aba102f25af333aff57a9 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Tue, 3 Nov 2020 08:43:53 +0300 Subject: [PATCH 08/39] Adding twoThreadsReady param to IndexGraphStarter. --- routing/index_graph_starter.cpp | 2 +- routing/index_graph_starter.hpp | 5 +++-- routing/index_router.cpp | 7 +++---- routing/routing_tests/index_graph_tools.cpp | 8 +++++--- routing/single_vehicle_world_graph.cpp | 1 + routing/single_vehicle_world_graph.hpp | 1 + routing/world_graph.hpp | 2 ++ 7 files changed, 16 insertions(+), 10 deletions(-) diff --git a/routing/index_graph_starter.cpp b/routing/index_graph_starter.cpp index 554786180d3..1482b8a3ae0 100644 --- a/routing/index_graph_starter.cpp +++ b/routing/index_graph_starter.cpp @@ -58,7 +58,7 @@ size_t IndexGraphStarter::GetRouteNumPoints(vector const & segments) IndexGraphStarter::IndexGraphStarter(FakeEnding const & startEnding, FakeEnding const & finishEnding, uint32_t fakeNumerationStart, - bool strictForward, bool twoThreadsReady, WorldGraph & graph) + bool strictForward, WorldGraph & graph) : m_graph(graph) { m_fakeNumerationStart = fakeNumerationStart; diff --git a/routing/index_graph_starter.hpp b/routing/index_graph_starter.hpp index cbeeb40ec3c..73786bb1268 100644 --- a/routing/index_graph_starter.hpp +++ b/routing/index_graph_starter.hpp @@ -57,8 +57,7 @@ class IndexGraphStarter : public AStarGraph & edges) const diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 48ee01177d9..25a9c818759 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -653,8 +653,7 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, uint32_t const fakeNumerationStart = starter ? starter->GetNumFakeSegments() + startIdx : startIdx; IndexGraphStarter subrouteStarter(startFakeEnding, finishFakeEnding, fakeNumerationStart, - isStartSegmentStrictForward, false /* twoThreadsReady */, - *graph); + isStartSegmentStrictForward, *graph); if (m_guides.IsAttached()) { @@ -913,7 +912,7 @@ RouterResultCode IndexRouter::AdjustRoute(Checkpoints const & checkpoints, FakeEnding dummy{}; IndexGraphStarter starter(MakeFakeEnding(startSegments, pointFrom, *graph), dummy, m_lastFakeEdges->GetNumFakeEdges(), bestSegmentIsAlmostCodirectional, - false /* twoThreadsReady */, *graph); + *graph); starter.Append(*m_lastFakeEdges); @@ -1004,7 +1003,7 @@ unique_ptr IndexRouter::MakeWorldGraph() if (m_vehicleType != VehicleType::Transit) { auto graph = make_unique( - move(crossMwmGraph), move(indexGraphLoader), m_estimator, + move(crossMwmGraph), move(indexGraphLoader), m_estimator, false /* twoThreadsReady */, MwmHierarchyHandler(m_numMwmIds, m_countryParentNameGetterFn)); graph->SetRoutingOptions(routingOptions); return graph; diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index cd7dc4ece54..0afdd73ccd9 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -420,7 +420,8 @@ unique_ptr BuildWorldGraph(unique_ptr(); indexLoader->AddGraph(kTestNumMwmId, move(graph)); return make_unique(nullptr /* crossMwmGraph */, move(indexLoader), - estimator, MwmHierarchyHandler(nullptr, nullptr)); + estimator, false /* twoThreadsReady */, + MwmHierarchyHandler(nullptr, nullptr)); } unique_ptr BuildIndexGraph(unique_ptr geometryLoader, @@ -442,7 +443,8 @@ unique_ptr BuildWorldGraph(unique_ptr(); indexLoader->AddGraph(kTestNumMwmId, move(graph)); return make_unique(nullptr /* crossMwmGraph */, move(indexLoader), - estimator, MwmHierarchyHandler(nullptr, nullptr)); + estimator, false /* twoThreadsReady */, + MwmHierarchyHandler(nullptr, nullptr)); } unique_ptr BuildWorldGraph(unique_ptr geometryLoader, @@ -620,7 +622,7 @@ unique_ptr MakeStarter(FakeEnding const & start, FakeEnding c WorldGraph & graph) { return make_unique(start, finish, 0 /* fakeNumerationStart */, - false /* strictForward */, false /* twoThreadsReady */, graph); + false /* strictForward */, graph); } time_t GetUnixtimeByDate(uint16_t year, Month month, uint8_t monthDay, uint8_t hours, diff --git a/routing/single_vehicle_world_graph.cpp b/routing/single_vehicle_world_graph.cpp index 4921e8b78eb..aab63538804 100644 --- a/routing/single_vehicle_world_graph.cpp +++ b/routing/single_vehicle_world_graph.cpp @@ -22,6 +22,7 @@ SingleVehicleWorldGraph::AStarParents::kEmpty = {}; SingleVehicleWorldGraph::SingleVehicleWorldGraph(unique_ptr crossMwmGraph, unique_ptr loader, shared_ptr estimator, + bool twoThreadsReady, MwmHierarchyHandler && hierarchyHandler) : m_crossMwmGraph(move(crossMwmGraph)) , m_loader(move(loader)) diff --git a/routing/single_vehicle_world_graph.hpp b/routing/single_vehicle_world_graph.hpp index 585aeb63a55..75772c27315 100644 --- a/routing/single_vehicle_world_graph.hpp +++ b/routing/single_vehicle_world_graph.hpp @@ -31,6 +31,7 @@ class SingleVehicleWorldGraph final : public WorldGraph SingleVehicleWorldGraph(std::unique_ptr crossMwmGraph, std::unique_ptr loader, std::shared_ptr estimator, + bool twoThreadsReady, MwmHierarchyHandler && hierarchyHandler); // WorldGraph overrides: diff --git a/routing/world_graph.hpp b/routing/world_graph.hpp index f3a136614a6..c1c29c66dc2 100644 --- a/routing/world_graph.hpp +++ b/routing/world_graph.hpp @@ -108,6 +108,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; From fa12c3360dc130a95eda0f5e8b09fb8aa273daf3 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Tue, 3 Nov 2020 14:41:15 +0300 Subject: [PATCH 09/39] Adding protection to IndexGraphLoaderImpl. --- routing/cross_mwm_graph.cpp | 1 + routing/index_graph_loader.cpp | 33 ++++++++++++++++---- routing/index_graph_loader.hpp | 4 ++- routing/index_router.cpp | 16 +++++----- routing/index_router.hpp | 2 +- routing/routing_tests/index_graph_tools.cpp | 5 ++- routing/routing_tests/index_graph_tools.hpp | 2 ++ routing/single_vehicle_world_graph.cpp | 2 +- routing/single_vehicle_world_graph.hpp | 2 +- routing/world_graph.cpp | 1 + track_analyzing/track_analyzer/cmd_table.cpp | 3 +- 11 files changed, 50 insertions(+), 21 deletions(-) diff --git a/routing/cross_mwm_graph.cpp b/routing/cross_mwm_graph.cpp index 51cb0bd25c5..435f47963f5 100644 --- a/routing/cross_mwm_graph.cpp +++ b/routing/cross_mwm_graph.cpp @@ -88,6 +88,7 @@ void CrossMwmGraph::DeserializeTransitTransitions(vector const & mwmId void CrossMwmGraph::GetTwins(Segment const & s, bool isOutgoing, vector & twins) { + // @TODO. This method may be called from two threads. It should be ready for it. ASSERT(IsTransition(s, isOutgoing), ("The segment", s, "is not a transition segment for isOutgoing ==", isOutgoing)); // Note. There's an extremely rare case when a segment is ingoing and outgoing at the same time. diff --git a/routing/index_graph_loader.cpp b/routing/index_graph_loader.cpp index 95adc4d4d6a..f76d23a79c8 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,7 +34,8 @@ using namespace std; class IndexGraphLoaderImpl final : public IndexGraphLoader { public: - IndexGraphLoaderImpl(VehicleType vehicleType, bool loadAltitudes, shared_ptr numMwmIds, + IndexGraphLoaderImpl(VehicleType vehicleType, bool loadAltitudes, bool isTwoThreadsReady, + shared_ptr numMwmIds, shared_ptr vehicleModelFactory, shared_ptr estimator, DataSource & dataSource, RoutingOptions routingOptions = RoutingOptions()); @@ -41,6 +45,7 @@ class IndexGraphLoaderImpl final : public IndexGraphLoader IndexGraph & GetIndexGraph(NumMwmId numMwmId) override; vector GetSpeedCameraInfo(Segment const & segment) override; void Clear() override; + bool IsTwoThreadsReady() const override; private: struct GraphAttrs @@ -59,6 +64,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 +77,7 @@ class IndexGraphLoaderImpl final : public IndexGraphLoader }; IndexGraphLoaderImpl::IndexGraphLoaderImpl( - VehicleType vehicleType, bool loadAltitudes, shared_ptr numMwmIds, + VehicleType vehicleType, bool loadAltitudes, bool isTwoThreadsReady, shared_ptr numMwmIds, shared_ptr vehicleModelFactory, shared_ptr estimator, DataSource & dataSource, RoutingOptions routingOptions) @@ -81,6 +87,7 @@ IndexGraphLoaderImpl::IndexGraphLoaderImpl( , m_numMwmIds(move(numMwmIds)) , m_vehicleModelFactory(move(vehicleModelFactory)) , m_estimator(move(estimator)) + , m_graphsMtx(isTwoThreadsReady ? std::optional() : std::nullopt) , m_avoidRoutingOptions(routingOptions) { CHECK(m_numMwmIds, ()); @@ -90,6 +97,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 +107,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 +115,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; } @@ -187,13 +198,16 @@ vector IndexGraphLoaderImpl::GetSpeedCameraInfo(Segme IndexGraphLoaderImpl::GraphAttrs & IndexGraphLoaderImpl::CreateGeometry(NumMwmId numMwmId) { platform::CountryFile const & file = m_numMwmIds->GetFile(numMwmId); + // @TODO No protection!!!!! MwmSet::MwmHandle handle = m_dataSource.GetMwmHandleByCountryFile(file); if (!handle.IsAlive()) MYTHROW(RoutingException, ("Can't get mwm handle for", file)); + // @TODO No protection!!!!! shared_ptr vehicleModel = m_vehicleModelFactory->GetVehicleModelForCountry(file.GetName()); + base::OptionalLockGuard guard(m_graphsMtx); auto & graph = m_graphs[numMwmId]; graph.m_geometry = make_shared(GeometryLoader::Create( m_dataSource, handle, vehicleModel, AttrLoader(m_dataSource, handle), m_loadAltitudes)); @@ -219,7 +233,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 +268,14 @@ namespace routing { // static unique_ptr IndexGraphLoader::Create( - VehicleType vehicleType, bool loadAltitudes, shared_ptr numMwmIds, + VehicleType vehicleType, bool loadAltitudes, bool isTwoThreadsReady, 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, isTwoThreadsReady, 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_router.cpp b/routing/index_router.cpp index 25a9c818759..0d8ac1cc608 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; } @@ -585,12 +585,14 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, return RouterResultCode::NeedMoreMaps; TrafficStash::Guard guard(m_trafficStash); - unique_ptr graph = MakeWorldGraph(); + unique_ptr graph = MakeWorldGraph(true /* isTwoThreadsReady */); vector segments; vector startSegments; bool startSegmentIsAlmostCodirectionalDirection = false; + // @TODO |startSegments| and |finishSegments| may be found in two threads with the help of + // |graph| with two threads support. bool const foundStart = FindBestSegments(checkpoints.GetPointFrom(), startDirection, true /* isOutgoing */, *graph, startSegments, startSegmentIsAlmostCodirectionalDirection); @@ -890,7 +892,7 @@ RouterResultCode IndexRouter::AdjustRoute(Checkpoints const & checkpoints, { base::Timer timer; TrafficStash::Guard guard(m_trafficStash); - auto graph = MakeWorldGraph(); + auto graph = MakeWorldGraph(false /* isTwoThreadsReady */); graph->SetMode(WorldGraphMode::NoLeaps); vector startSegments; @@ -981,7 +983,7 @@ RouterResultCode IndexRouter::AdjustRoute(Checkpoints const & checkpoints, return RouterResultCode::NoError; } -unique_ptr IndexRouter::MakeWorldGraph() +unique_ptr IndexRouter::MakeWorldGraph(bool isTwoThreadsReady) { RoutingOptions routingOptions; if (m_vehicleType == VehicleType::Car) @@ -997,13 +999,13 @@ 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, isTwoThreadsReady, m_numMwmIds, m_vehicleModelFactory, + m_estimator, m_dataSource, routingOptions); if (m_vehicleType != VehicleType::Transit) { auto graph = make_unique( - move(crossMwmGraph), move(indexGraphLoader), m_estimator, false /* twoThreadsReady */, + move(crossMwmGraph), move(indexGraphLoader), m_estimator, MwmHierarchyHandler(m_numMwmIds, m_countryParentNameGetterFn)); graph->SetRoutingOptions(routingOptions); return graph; diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 84439914519..15a0b55a0c0 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -128,7 +128,7 @@ class IndexRouter : public IRouter m2::PointD const & startDirection, RouterDelegate const & delegate, Route & route); - std::unique_ptr MakeWorldGraph(); + std::unique_ptr MakeWorldGraph(bool isTwoThreadsReady); /// \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. diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index 0afdd73ccd9..7826cacf618 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -420,7 +420,7 @@ unique_ptr BuildWorldGraph(unique_ptr(); indexLoader->AddGraph(kTestNumMwmId, move(graph)); return make_unique(nullptr /* crossMwmGraph */, move(indexLoader), - estimator, false /* twoThreadsReady */, + estimator, MwmHierarchyHandler(nullptr, nullptr)); } @@ -443,8 +443,7 @@ unique_ptr BuildWorldGraph(unique_ptr(); indexLoader->AddGraph(kTestNumMwmId, move(graph)); return make_unique(nullptr /* crossMwmGraph */, move(indexLoader), - estimator, false /* twoThreadsReady */, - MwmHierarchyHandler(nullptr, nullptr)); + estimator, MwmHierarchyHandler(nullptr, nullptr)); } unique_ptr BuildWorldGraph(unique_ptr geometryLoader, diff --git a/routing/routing_tests/index_graph_tools.hpp b/routing/routing_tests/index_graph_tools.hpp index 5040823739b..210d72c6915 100644 --- a/routing/routing_tests/index_graph_tools.hpp +++ b/routing/routing_tests/index_graph_tools.hpp @@ -143,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/single_vehicle_world_graph.cpp b/routing/single_vehicle_world_graph.cpp index aab63538804..f5b51759109 100644 --- a/routing/single_vehicle_world_graph.cpp +++ b/routing/single_vehicle_world_graph.cpp @@ -22,7 +22,6 @@ SingleVehicleWorldGraph::AStarParents::kEmpty = {}; SingleVehicleWorldGraph::SingleVehicleWorldGraph(unique_ptr crossMwmGraph, unique_ptr loader, shared_ptr estimator, - bool twoThreadsReady, MwmHierarchyHandler && hierarchyHandler) : m_crossMwmGraph(move(crossMwmGraph)) , m_loader(move(loader)) @@ -213,6 +212,7 @@ RoadGeometry const & SingleVehicleWorldGraph::GetRoadGeometry(NumMwmId mwmId, ui void SingleVehicleWorldGraph::GetTwinsInner(Segment const & segment, bool isOutgoing, vector & twins) { + // @TODO It should be ready for calling form two threads if IsTwoThreadsReady() returns true. m_crossMwmGraph->GetTwins(segment, isOutgoing, twins); } diff --git a/routing/single_vehicle_world_graph.hpp b/routing/single_vehicle_world_graph.hpp index 75772c27315..194e48bf5d8 100644 --- a/routing/single_vehicle_world_graph.hpp +++ b/routing/single_vehicle_world_graph.hpp @@ -31,7 +31,6 @@ class SingleVehicleWorldGraph final : public WorldGraph SingleVehicleWorldGraph(std::unique_ptr crossMwmGraph, std::unique_ptr loader, std::shared_ptr estimator, - bool twoThreadsReady, MwmHierarchyHandler && hierarchyHandler); // WorldGraph overrides: @@ -97,6 +96,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 diff --git a/routing/world_graph.cpp b/routing/world_graph.cpp index 6eae0778ff4..f7a3e07ada8 100644 --- a/routing/world_graph.cpp +++ b/routing/world_graph.cpp @@ -14,6 +14,7 @@ void WorldGraph::GetEdgeList(Segment const & vertex, bool isOutgoing, bool useRo void WorldGraph::GetTwins(Segment const & segment, bool isOutgoing, bool useRoutingOptions, std::vector & edges) { + // @TODO It should be ready for calling form two threads if IsTwoThreadsReady() returns true. std::vector twins; GetTwinsInner(segment, isOutgoing, twins); 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); From 0942287bb59c57ea6c7829b529c23eea7d95f05a Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Thu, 5 Nov 2020 05:43:01 +0300 Subject: [PATCH 10/39] Passing twoThreadsReady param to Geometry. --- .../generator_tests_support/routing_helpers.cpp | 2 +- routing/geometry.cpp | 17 ++++++++--------- routing/geometry.hpp | 5 ++--- routing/index_graph.cpp | 1 + routing/index_graph_loader.cpp | 5 +++-- routing/routing_tests/index_graph_tools.cpp | 2 +- routing/single_vehicle_world_graph.cpp | 3 ++- 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/generator/generator_tests_support/routing_helpers.cpp b/generator/generator_tests_support/routing_helpers.cpp index a2bbf5fff49..7a81d6af8bb 100644 --- a/generator/generator_tests_support/routing_helpers.cpp +++ b/generator/generator_tests_support/routing_helpers.cpp @@ -98,7 +98,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/routing/geometry.cpp b/routing/geometry.cpp index 5e46e7bf8ae..4f7d47878aa 100644 --- a/routing/geometry.cpp +++ b/routing/geometry.cpp @@ -60,8 +60,8 @@ 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); // GeometryLoader overrides: void Load(uint32_t featureId, RoadGeometry & road) override; @@ -253,8 +253,8 @@ double RoadGeometry::GetRoadLengthM() const } // Geometry ---------------------------------------------------------------------------------------- -Geometry::Geometry(unique_ptr loader) - : m_loader(move(loader)) +Geometry::Geometry(std::unique_ptr loader, bool twoThreadsReady) + : m_loader(move(loader)) , m_featureIdToRoad(make_unique( kRoadsCacheSize, [this](uint32_t featureId, RoadGeometry & road) { m_loader->Load(featureId, road); })) @@ -271,11 +271,10 @@ RoadGeometry const & Geometry::GetRoad(uint32_t featureId, bool 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) { CHECK(handle.IsAlive(), ()); return make_unique(dataSource, handle, vehicleModel, move(attrLoader), diff --git a/routing/geometry.hpp b/routing/geometry.hpp index baf3b126711..a2e9a126178 100644 --- a/routing/geometry.hpp +++ b/routing/geometry.hpp @@ -114,8 +114,7 @@ class GeometryLoader static std::unique_ptr Create(DataSource const & dataSource, MwmSet::MwmHandle const & handle, std::shared_ptr vehicleModel, - AttrLoader && attrLoader, - bool loadAltitudes); + AttrLoader && attrLoader, bool loadAltitudes); /// This is for stand-alone work. /// Use in generator_tool and unit tests. @@ -135,7 +134,7 @@ 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. diff --git a/routing/index_graph.cpp b/routing/index_graph.cpp index bbc88d8206c..c65fc19357f 100644 --- a/routing/index_graph.cpp +++ b/routing/index_graph.cpp @@ -181,6 +181,7 @@ optional IndexGraph::GetJointEdgeByLastPoint(Segment const & parent, Segment const & firstChild, bool isOutgoing, uint32_t lastPoint) { + // @TODO It should be ready for calling form two threads if IsTwoThreadsReady() returns true. vector const possibleChildren = {firstChild}; vector const lastPoints = {lastPoint}; diff --git a/routing/index_graph_loader.cpp b/routing/index_graph_loader.cpp index f76d23a79c8..39e3a826076 100644 --- a/routing/index_graph_loader.cpp +++ b/routing/index_graph_loader.cpp @@ -209,8 +209,9 @@ IndexGraphLoaderImpl::GraphAttrs & IndexGraphLoaderImpl::CreateGeometry(NumMwmId base::OptionalLockGuard guard(m_graphsMtx); 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()); return graph; } diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index 7826cacf618..9fc00a83d31 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -438,7 +438,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)); diff --git a/routing/single_vehicle_world_graph.cpp b/routing/single_vehicle_world_graph.cpp index f5b51759109..973584b594f 100644 --- a/routing/single_vehicle_world_graph.cpp +++ b/routing/single_vehicle_world_graph.cpp @@ -91,6 +91,7 @@ void SingleVehicleWorldGraph::GetEdgeList( astar::VertexData const & vertexData, bool isOutgoing, bool useRoutingOptions, bool useAccessConditional, vector & edges) { + // @TODO It should be ready for calling form two threads if IsTwoThreadsReady() returns true. CHECK_NOT_EQUAL(m_mode, WorldGraphMode::LeapsOnly, ()); ASSERT(m_parentsForSegments.forward && m_parentsForSegments.backward, @@ -114,7 +115,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()); From 8be16db1d5e5b683568bee45b2d4e78af3b23c75 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Thu, 5 Nov 2020 11:04:03 +0300 Subject: [PATCH 11/39] Adding backward cache to Geomtry. --- .../routing_helpers.cpp | 3 +- .../routing_helpers.hpp | 2 +- routing/geometry.cpp | 65 ++++++++++++++----- routing/geometry.hpp | 9 ++- routing/index_graph_loader.cpp | 2 +- routing/routing_tests/index_graph_tools.cpp | 4 +- routing/routing_tests/index_graph_tools.hpp | 2 +- track_analyzing/track_matcher.cpp | 2 +- 8 files changed, 65 insertions(+), 24 deletions(-) diff --git a/generator/generator_tests_support/routing_helpers.cpp b/generator/generator_tests_support/routing_helpers.cpp index 7a81d6af8bb..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; 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/routing/geometry.cpp b/routing/geometry.cpp index 4f7d47878aa..04892b051f3 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) { @@ -61,15 +63,18 @@ class GeometryLoaderImpl final : public GeometryLoader public: GeometryLoaderImpl(DataSource const & dataSource, MwmSet::MwmHandle const & handle, shared_ptr vehicleModel, AttrLoader attrLoader, - bool loadAltitudes); + 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,10 +83,13 @@ 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_loadAltitudes(loadAltitudes) @@ -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())); + // TODO Altitude should be refactored to be able to be accessible from two threads. +// if (m_loadAltitudes) +// altitudes = &(m_altitudeLoader.GetAltitudes(featureId, feature->GetPointsCount())); road.Load(*m_vehicleModel, *feature, altitudes, m_attrLoader.m_cityRoads->IsCityRoad(featureId), m_attrLoader.m_maxspeeds->GetMaxspeed(featureId)); - m_altitudeLoader.ClearCache(); +// m_altitudeLoader.ClearCache(); } // 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); @@ -255,9 +274,18 @@ double RoadGeometry::GetRoadLengthM() const // Geometry ---------------------------------------------------------------------------------------- Geometry::Geometry(std::unique_ptr loader, bool twoThreadsReady) : m_loader(move(loader)) - , m_featureIdToRoad(make_unique( - kRoadsCacheSize, - [this](uint32_t featureId, RoadGeometry & road) { m_loader->Load(featureId, road); })) + , m_featureIdToRoad( + make_unique(twoThreadsReady ? kRoadsCacheSizeTwoCaches : kRoadsCacheSize, + [this](uint32_t featureId, RoadGeometry & road) { + m_loader->Load(featureId, road, true /* isOutgoing */); + })) + , m_featureIdToRoadBwd(twoThreadsReady + ? make_unique( + kRoadsCacheSizeTwoCaches, + [this](uint32_t featureId, RoadGeometry & road) { + m_loader->Load(featureId, road, false /* isOutgoing */); + }) + : nullptr) { CHECK(m_loader, ()); } @@ -267,6 +295,11 @@ 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); } @@ -274,11 +307,11 @@ RoadGeometry const & Geometry::GetRoad(uint32_t featureId, bool isOutgoing) unique_ptr GeometryLoader::Create( DataSource const & dataSource, MwmSet::MwmHandle const & handle, std::shared_ptr vehicleModel, AttrLoader && attrLoader, - bool loadAltitudes) + 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 a2e9a126178..b553187bca3 100644 --- a/routing/geometry.hpp +++ b/routing/geometry.hpp @@ -108,13 +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. @@ -148,10 +149,14 @@ class Geometry final } private: + bool IsTwoThreadsReady() const { return m_featureIdToRoadBwd != nullptr; } + using RoutingFifoCache = FifoCache>; 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/index_graph_loader.cpp b/routing/index_graph_loader.cpp index 39e3a826076..1d3726225be 100644 --- a/routing/index_graph_loader.cpp +++ b/routing/index_graph_loader.cpp @@ -211,7 +211,7 @@ IndexGraphLoaderImpl::GraphAttrs & IndexGraphLoaderImpl::CreateGeometry(NumMwmId auto & graph = m_graphs[numMwmId]; graph.m_geometry = make_shared( GeometryLoader::Create(m_dataSource, handle, vehicleModel, AttrLoader(m_dataSource, handle), - m_loadAltitudes), IsTwoThreadsReady()); + m_loadAltitudes, IsTwoThreadsReady()), IsTwoThreadsReady()); return graph; } diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index 9fc00a83d31..cb6269ea359 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -101,8 +101,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); diff --git a/routing/routing_tests/index_graph_tools.hpp b/routing/routing_tests/index_graph_tools.hpp index 210d72c6915..b5aec943a74 100644 --- a/routing/routing_tests/index_graph_tools.hpp +++ b/routing/routing_tests/index_graph_tools.hpp @@ -125,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 diff --git a/track_analyzing/track_matcher.cpp b/track_analyzing/track_matcher.cpp index 52d803133fd..dc4bca322bb 100644 --- a/track_analyzing/track_matcher.cpp +++ b/track_analyzing/track_matcher.cpp @@ -71,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); From 828e405781ff965125efa3e0fb5e4440ef996463 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Thu, 5 Nov 2020 11:20:57 +0300 Subject: [PATCH 12/39] Adding test code for route building. --- routing/index_router.cpp | 17 +++++++++++++---- routing/index_router.hpp | 5 +++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 0d8ac1cc608..0c12971c5c3 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -407,7 +407,15 @@ RouterResultCode IndexRouter::CalculateRoute(Checkpoints const & checkpoints, } } - return DoCalculateRoute(checkpoints, startDirection, delegate, route); + RouterResultCode lastReturn = RouterResultCode::InternalError; + for (auto twoThreadsReady : {false, true, true, false}) + { + LOG(LINFO, ("---------------------", twoThreadsReady ? "Two threads" : "One threads", "---------------------")); + lastReturn = DoCalculateRoute(checkpoints, startDirection, delegate, twoThreadsReady, route); + LOG(LINFO, ("---------------------", twoThreadsReady ? "Two threads" : "One threads", "END---------------------")); + } +// return DoCalculateRoute(checkpoints, startDirection, delegate, false, route); + return lastReturn; } catch (RootException const & e) { @@ -552,7 +560,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 twoThreadsReady, Route & route) { m_lastRoute.reset(); // MwmId used for guides segments in RedressRoute(). @@ -983,7 +992,7 @@ RouterResultCode IndexRouter::AdjustRoute(Checkpoints const & checkpoints, return RouterResultCode::NoError; } -unique_ptr IndexRouter::MakeWorldGraph(bool isTwoThreadsReady) +unique_ptr IndexRouter::MakeWorldGraph(bool twoThreadsReady) { RoutingOptions routingOptions; if (m_vehicleType == VehicleType::Car) @@ -999,7 +1008,7 @@ unique_ptr IndexRouter::MakeWorldGraph(bool isTwoThreadsReady) auto indexGraphLoader = IndexGraphLoader::Create( m_vehicleType == VehicleType::Transit ? VehicleType::Pedestrian : m_vehicleType, - m_loadAltitudes, isTwoThreadsReady, m_numMwmIds, m_vehicleModelFactory, + m_loadAltitudes, twoThreadsReady, m_numMwmIds, m_vehicleModelFactory, m_estimator, m_dataSource, routingOptions); if (m_vehicleType != VehicleType::Transit) diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 15a0b55a0c0..d8fbcc92f43 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -117,7 +117,8 @@ class IndexRouter : public IRouter RouterResultCode DoCalculateRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, - RouterDelegate const & delegate, Route & route); + RouterDelegate const & delegate, bool twoThreadsReady, + Route & route); RouterResultCode CalculateSubroute(Checkpoints const & checkpoints, size_t subrouteIdx, RouterDelegate const & delegate, std::shared_ptr const & progress, @@ -128,7 +129,7 @@ class IndexRouter : public IRouter m2::PointD const & startDirection, RouterDelegate const & delegate, Route & route); - std::unique_ptr MakeWorldGraph(bool isTwoThreadsReady); + 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. From d7bfc7bfc3d1aad252276a4a08c1dc73e4178200 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Thu, 5 Nov 2020 18:04:04 +0300 Subject: [PATCH 13/39] Passing param useTwoThreads to FindPathBidirectional and fixing locked twice mutex. --- routing/base/astar_algorithm.hpp | 19 +++++++++++---- routing/index_graph_loader.cpp | 5 +--- routing/index_graph_starter_joints.hpp | 2 +- routing/index_router.cpp | 23 ++++++++++++------- routing/index_router.hpp | 13 +++++++---- .../routing_tests/astar_algorithm_test.cpp | 5 ++-- routing/routing_tests/index_graph_tools.cpp | 9 +++++--- routing/routing_tests/routing_algorithm.cpp | 3 ++- 8 files changed, 51 insertions(+), 28 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index b8b693ed0b9..eefb6332b76 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -210,13 +210,13 @@ class AStarAlgorithm Result FindPath(P & params, RoutingResult & result) const; template - Result FindPathBidirectional(P & params, RoutingResult & result) const; + Result FindPathBidirectional(bool useTwoThreads, 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. @@ -550,7 +550,7 @@ AStarAlgorithm::FindPath(P & params, RoutingResult template typename AStarAlgorithm::Result -AStarAlgorithm::FindPathBidirectional(P & params, +AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, P & params, RoutingResult & result) const { auto const epsilon = params.m_weightEpsilon; @@ -558,6 +558,17 @@ AStarAlgorithm::FindPathBidirectional(P & params, auto const & finalVertex = params.m_finalVertex; auto const & startVertex = params.m_startVertex; + if (useTwoThreads) + { + CHECK(graph.IsTwoThreadsReady(), + ("For two threads routing it's necessary to use two threads ready graph.")); + } + else + { + CHECK(!graph.IsTwoThreadsReady(), + ("Only one thread will be used. You may still use two threads graph but it brings some performance leaks.")); + } + BidirectionalStepContext forward(true /* forward */, startVertex, finalVertex, graph); BidirectionalStepContext backward(false /* forward */, startVertex, finalVertex, graph); diff --git a/routing/index_graph_loader.cpp b/routing/index_graph_loader.cpp index 1d3726225be..231a038875a 100644 --- a/routing/index_graph_loader.cpp +++ b/routing/index_graph_loader.cpp @@ -87,7 +87,7 @@ IndexGraphLoaderImpl::IndexGraphLoaderImpl( , m_numMwmIds(move(numMwmIds)) , m_vehicleModelFactory(move(vehicleModelFactory)) , m_estimator(move(estimator)) - , m_graphsMtx(isTwoThreadsReady ? std::optional() : std::nullopt) + , m_graphsMtx(isTwoThreadsReady ? std::make_optional() : std::nullopt) , m_avoidRoutingOptions(routingOptions) { CHECK(m_numMwmIds, ()); @@ -198,16 +198,13 @@ vector IndexGraphLoaderImpl::GetSpeedCameraInfo(Segme IndexGraphLoaderImpl::GraphAttrs & IndexGraphLoaderImpl::CreateGeometry(NumMwmId numMwmId) { platform::CountryFile const & file = m_numMwmIds->GetFile(numMwmId); - // @TODO No protection!!!!! MwmSet::MwmHandle handle = m_dataSource.GetMwmHandleByCountryFile(file); if (!handle.IsAlive()) MYTHROW(RoutingException, ("Can't get mwm handle for", file)); - // @TODO No protection!!!!! shared_ptr vehicleModel = m_vehicleModelFactory->GetVehicleModelForCountry(file.GetName()); - base::OptionalLockGuard guard(m_graphsMtx); auto & graph = m_graphs[numMwmId]; graph.m_geometry = make_shared( GeometryLoader::Create(m_dataSource, handle, vehicleModel, AttrLoader(m_dataSource, handle), diff --git a/routing/index_graph_starter_joints.hpp b/routing/index_graph_starter_joints.hpp index d355db67332..3241ed85308 100644 --- a/routing/index_graph_starter_joints.hpp +++ b/routing/index_graph_starter_joints.hpp @@ -245,7 +245,7 @@ class IndexGraphStarterJoints : public AStarGraph std::optional GetOptionalMutex(Graph const & graph) { - return graph.IsTwoThreadsReady() ? std::optional() : std::nullopt; + return graph.IsTwoThreadsReady() ? std::make_optional() : std::nullopt; } template diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 0c12971c5c3..d444c4b1d3c 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -411,6 +411,8 @@ RouterResultCode IndexRouter::CalculateRoute(Checkpoints const & checkpoints, for (auto twoThreadsReady : {false, true, true, false}) { LOG(LINFO, ("---------------------", twoThreadsReady ? "Two threads" : "One threads", "---------------------")); + // TODO |twoThreadsReady| is passed to DoCalculateRoute(), CalculateSubroute() and + // to CalculateSubrouteJointsMode() for test purposes only. lastReturn = DoCalculateRoute(checkpoints, startDirection, delegate, twoThreadsReady, route); LOG(LINFO, ("---------------------", twoThreadsReady ? "Two threads" : "One threads", "END---------------------")); } @@ -594,7 +596,7 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, return RouterResultCode::NeedMoreMaps; TrafficStash::Guard guard(m_trafficStash); - unique_ptr graph = MakeWorldGraph(true /* isTwoThreadsReady */); + unique_ptr graph = MakeWorldGraph(twoThreadsReady); vector segments; @@ -686,7 +688,7 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, SCOPE_GUARD(eraseProgress, [&progress]() { progress->PushAndDropLastSubProgress(); }); auto const result = CalculateSubroute(checkpoints, i, delegate, progress, subrouteStarter, - subroute, m_guides.IsAttached()); + subroute, twoThreadsReady, m_guides.IsAttached()); if (result != RouterResultCode::NoError) return result; @@ -767,6 +769,7 @@ RouterResultCode IndexRouter::CalculateSubroute(Checkpoints const & checkpoints, shared_ptr const & progress, IndexGraphStarter & starter, vector & subroute, + bool twoThreadsReady, bool guidesActive /* = false */) { CHECK(progress, (checkpoints)); @@ -780,7 +783,7 @@ RouterResultCode IndexRouter::CalculateSubroute(Checkpoints const & checkpoints, switch (mode) { case WorldGraphMode::Joints: - return CalculateSubrouteJointsMode(starter, delegate, progress, subroute); + return CalculateSubrouteJointsMode(starter, delegate, progress, twoThreadsReady, subroute); case WorldGraphMode::NoLeaps: return CalculateSubrouteNoLeapsMode(starter, delegate, progress, subroute); case WorldGraphMode::LeapsOnly: @@ -793,7 +796,7 @@ RouterResultCode IndexRouter::CalculateSubroute(Checkpoints const & checkpoints, RouterResultCode IndexRouter::CalculateSubrouteJointsMode( IndexGraphStarter & starter, RouterDelegate const & delegate, - shared_ptr const & progress, vector & subroute) + shared_ptr const & progress, bool twoThreadsReady, vector & subroute) { using JointsStarter = IndexGraphStarterJoints; JointsStarter jointStarter(starter, starter.GetStartSegment(), starter.GetFinishSegment()); @@ -812,7 +815,8 @@ RouterResultCode IndexRouter::CalculateSubrouteJointsMode( RoutingResult routingResult; RouterResultCode const result = - FindPath(params, {} /* mwmIds */, routingResult, WorldGraphMode::Joints); + FindPath(twoThreadsReady, params, + {} /* mwmIds */, routingResult, WorldGraphMode::Joints); if (result != RouterResultCode::NoError) return result; @@ -839,7 +843,8 @@ RouterResultCode IndexRouter::CalculateSubrouteNoLeapsMode( RoutingResult routingResult; set const mwmIds = starter.GetMwms(); RouterResultCode const result = - FindPath(params, mwmIds, routingResult, WorldGraphMode::NoLeaps); + FindPath(false /* useTwoThreads */, params, mwmIds, routingResult, + WorldGraphMode::NoLeaps); if (result != RouterResultCode::NoError) return result; @@ -874,7 +879,8 @@ RouterResultCode IndexRouter::CalculateSubrouteLeapsOnlyMode( RoutingResult routingResult; RouterResultCode const result = - FindPath(params, {} /* mwmIds */, routingResult, WorldGraphMode::LeapsOnly); + FindPath(false /* useTwoThreads */, params, {} /* mwmIds */, + routingResult, WorldGraphMode::LeapsOnly); progress->PushAndDropLastSubProgress(); @@ -1362,7 +1368,8 @@ RouterResultCode IndexRouter::ProcessLeapsJoints(vector const & input, nullptr /* prevRoute */, delegate.GetCancellable(), move(visitor), AStarLengthChecker(starter)); - resultCode = FindPath(params, mwmIds, routingResult, mode); + resultCode = FindPath(true /* useTwoThreads */, params, mwmIds, + routingResult, mode); return resultCode; }; diff --git a/routing/index_router.hpp b/routing/index_router.hpp index d8fbcc92f43..0c4a2098ddc 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -104,6 +104,7 @@ class IndexRouter : public IRouter RouterResultCode CalculateSubrouteJointsMode(IndexGraphStarter & starter, RouterDelegate const & delegate, std::shared_ptr const & progress, + bool twoThreadsReady, std::vector & subroute); RouterResultCode CalculateSubrouteNoLeapsMode(IndexGraphStarter & starter, RouterDelegate const & delegate, @@ -123,7 +124,7 @@ class IndexRouter : public IRouter RouterDelegate const & delegate, std::shared_ptr const & progress, IndexGraphStarter & graph, std::vector & subroute, - bool guidesActive = false); + bool twoThreadsReady, bool guidesActive = false); RouterResultCode AdjustRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, @@ -208,13 +209,15 @@ class IndexRouter : public IRouter } template - RouterResultCode FindPath( - AStarParams & params, std::set const & mwmIds, - RoutingResult & routingResult, WorldGraphMode mode) const + RouterResultCode FindPath(bool useTwoThreads, 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(useTwoThreads, params, routingResult))); } void SetupAlgorithmMode(IndexGraphStarter & starter, bool guidesActive = false) const; diff --git a/routing/routing_tests/astar_algorithm_test.cpp b/routing/routing_tests/astar_algorithm_test.cpp index 33039b22b16..aca87026ef4 100644 --- a/routing/routing_tests/astar_algorithm_test.cpp +++ b/routing/routing_tests/astar_algorithm_test.cpp @@ -31,7 +31,8 @@ void TestAStar(UndirectedGraph & graph, vector const & expectedRoute, TEST_ALMOST_EQUAL_ULPS(expectedDistance, actualRoute.m_distance, ()); actualRoute.m_path.clear(); - TEST_EQUAL(Algorithm::Result::OK, algo.FindPathBidirectional(params, actualRoute), ()); + TEST_EQUAL(Algorithm::Result::OK, + algo.FindPathBidirectional(false /* useTwoThreads */, params, actualRoute), ()); TEST_EQUAL(expectedRoute, actualRoute.m_path, ()); TEST_ALMOST_EQUAL_ULPS(expectedDistance, actualRoute.m_distance, ()); } @@ -75,7 +76,7 @@ UNIT_TEST(AStarAlgorithm_CheckLength) TEST_EQUAL(result, Algorithm::Result::NoPath, ()); routingResult = {}; - result = algo.FindPathBidirectional(params, routingResult); + result = algo.FindPathBidirectional(false /* useTwoThreads */, params, routingResult); // Best route weight is 23 so we expect to find no route with restriction |weight < 23|. TEST_EQUAL(result, Algorithm::Result::NoPath, ()); } diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index cb6269ea359..bcc3b4d4245 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -84,7 +84,8 @@ void NoUTurnRestrictionTest::TestRouteGeom(Segment const & start, Segment const nullptr /* prevRoute */); RoutingResult routingResult; - auto const resultCode = algorithm.FindPathBidirectional(params, routingResult); + auto const resultCode = + algorithm.FindPathBidirectional(false /* useTwoThreads */, params, routingResult); TEST_EQUAL(resultCode, expectedRouteResult, ()); for (size_t i = 0; i < routingResult.m_path.size(); ++i) @@ -277,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(false /* useTwoThreads */, params, routingResult); // Check unidirectional AStar returns same result. { @@ -482,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(false /* useTwoThreads */, params, routingResult); timeSec = routingResult.m_distance.GetWeight(); roadPoints = routingResult.m_path; diff --git a/routing/routing_tests/routing_algorithm.cpp b/routing/routing_tests/routing_algorithm.cpp index fdca5decc10..a70e7151da0 100644 --- a/routing/routing_tests/routing_algorithm.cpp +++ b/routing/routing_tests/routing_algorithm.cpp @@ -215,7 +215,8 @@ TestAStarBidirectionalAlgo::Result TestAStarBidirectionalAlgo::CalculateRoute( Algorithm::Params<> params(roadGraph, startPos, finalPos, {} /* prevRoute */, cancellable); - Algorithm::Result const res = Algorithm().FindPathBidirectional(params, path); + // TODO. Add two threads tests. + Algorithm::Result const res = Algorithm().FindPathBidirectional(false /* useTwoThreads */, params, path); return Convert(res); } } // namespace routing From 5d38d8bbaf23a66f787a55279f454c8d70473c39 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Fri, 6 Nov 2020 04:32:36 +0300 Subject: [PATCH 14/39] Preparing BidirectionalStepContext for two threading. --- routing/base/astar_algorithm.hpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index eefb6332b76..94d45e6065e 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -8,6 +8,7 @@ #include "base/assert.hpp" #include "base/cancellable.hpp" #include "base/logging.hpp" +#include "base/optional_lock_guard.hpp" #include #include @@ -267,12 +268,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); @@ -304,8 +302,9 @@ class AStarAlgorithm Weight ConsistentHeuristic(Vertex const & v) const { // TODO. The last parameter may be false in case of two thread routing. - auto const piF = graph.HeuristicCostEstimate(v, finalVertex, true /* forward */); - auto const piR = graph.HeuristicCostEstimate(v, startVertex, true /* forward */); + 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 @@ -322,17 +321,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; } @@ -354,6 +356,9 @@ class AStarAlgorithm Parents & GetParents() { return parent; } + bool IsTwoThreadsReady() const { return mtx.has_value(); } + + std::optional & mtx; bool const forward; Vertex const & startVertex; Vertex const & finalVertex; @@ -558,10 +563,12 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, auto const & finalVertex = params.m_finalVertex; auto const & startVertex = params.m_startVertex; + std::optional mtx; if (useTwoThreads) { CHECK(graph.IsTwoThreadsReady(), ("For two threads routing it's necessary to use two threads ready graph.")); + mtx.emplace(); } else { @@ -569,8 +576,8 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, ("Only one thread will be used. You may still use two threads graph but it brings some performance leaks.")); } - BidirectionalStepContext forward(true /* forward */, startVertex, finalVertex, graph); - BidirectionalStepContext backward(false /* forward */, startVertex, finalVertex, graph); + BidirectionalStepContext forward(mtx, true /* forward */, startVertex, finalVertex, graph); + BidirectionalStepContext backward(mtx, false /* forward */, startVertex, finalVertex, graph); auto & forwardParents = forward.GetParents(); auto & backwardParents = backward.GetParents(); From 14747daeb503b82dc451dc79a37bb234bd23235c Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Fri, 6 Nov 2020 06:46:52 +0300 Subject: [PATCH 15/39] Implementing two threads in bidirectional astar. --- routing/base/astar_algorithm.hpp | 93 +++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index 94d45e6065e..c0673c361da 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -11,7 +11,9 @@ #include "base/optional_lock_guard.hpp" #include +#include #include +#include #include #include #include @@ -568,7 +570,6 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, { CHECK(graph.IsTwoThreadsReady(), ("For two threads routing it's necessary to use two threads ready graph.")); - mtx.emplace(); } else { @@ -592,6 +593,96 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, backward.UpdateDistance(State(finalVertex, kZeroDistance)); backward.queue.push(State(finalVertex, kZeroDistance, backward.ConsistentHeuristic(finalVertex))); + if (useTwoThreads) + { + mtx.emplace(); + ////////////////////////////////////////////////////////////////////////////////////////////////// + auto wave = [&epsilon](BidirectionalStepContext & context, std::atomic & queueIsEmpty, + BidirectionalStepContext & oppositeContext, + std::atomic & oppositeQueueIsEmpty) { + LOG(LINFO, ("---FindPath-------wave---------------------------", + context.forward ? "forward" : "backward")); + size_t i = 0; + std::vector adj; + while (!context.queue.empty() && !oppositeQueueIsEmpty.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.forward ? context.finalVertex : context.startVertex); + context.GetAdjacencyList(stateV, adj); + auto const & pV = stateV.heuristic; + for (auto const & edge : 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)) + // continue; + + if (context.ExistsStateWithBetterDistance(stateW, epsilon)) + continue; + + stateW.heuristic = pW; + context.UpdateDistance(stateW); + context.UpdateParent(stateW.vertex, stateV.vertex); + ++i; + if (oppositeContext.GetDistance(stateW.vertex)) + { + LOG(LINFO, ("---FindPath-------wave end---------------------------", + context.forward ? "forward" : "backward", stateW.vertex, i)); + return; // The waves are intersected. + } + + if (stateW.vertex != (context.forward ? context.finalVertex : context.startVertex)) + context.queue.push(stateW); + } + } + + if (context.queue.empty()) + queueIsEmpty.store(true); + LOG(LINFO, ("----FindPath------wave end (empty)---------------------------", + context.forward ? "forward" : "backward")); + }; + + std::atomic fwdIsEmpty; + std::atomic bwdIsEmpty; + + PeriodicPollCancellable periodicCancellable(params.m_cancellable); + + // Starting two wave in two threads. + { + base::ScopedTimerWithLog timer("Wave"); + auto backwardWave = std::async(std::launch::async, wave, std::ref(backward), + std::ref(bwdIsEmpty), std::ref(forward), std::ref(fwdIsEmpty)); + wave(forward, fwdIsEmpty, backward, bwdIsEmpty); + backwardWave.get(); + } + LOG(LINFO, ("-------FindPath-------Finished-----------------")); + ////////////////////////////////////////////////////////////////////////////////////////////////// + mtx.reset(); + CHECK(!mtx.has_value(), ()); + } + // To use the search code both for backward and forward directions // we keep the pointers to everything related to the search in the // 'current' and 'next' directions. Swapping these pointers indicates From 804d9b921a75f0a828678199f0caf7a287944045 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Fri, 6 Nov 2020 09:32:02 +0300 Subject: [PATCH 16/39] Adding some comments --- routing/base/astar_algorithm.hpp | 6 ++++++ routing/index_router.cpp | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index c0673c361da..9073f33a545 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -597,6 +597,9 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, { mtx.emplace(); ////////////////////////////////////////////////////////////////////////////////////////////////// + // @TODO The multithreading code below in wave lambda is used more or less the same line + // as one thread bidirectional version. Consider put in some functions and call them for + // one thread and two thread versions. auto wave = [&epsilon](BidirectionalStepContext & context, std::atomic & queueIsEmpty, BidirectionalStepContext & oppositeContext, std::atomic & oppositeQueueIsEmpty) { @@ -614,6 +617,7 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, if (context.ExistsStateWithBetterDistance(stateV)) continue; + // @TODO This call should be synchronized in two thread case. // params.m_onVisitedVertexCallback(stateV.vertex, context.forward ? context.finalVertex : context.startVertex); context.GetAdjacencyList(stateV, adj); auto const & pV = stateV.heuristic; @@ -634,6 +638,7 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, stateW.distance = stateV.distance + std::max(reducedWeight, kZeroDistance); + // @TODO This call should be synchronized in two thread case. // auto const fullLength = weight + stateV.distance + context.pS - pV; // if (!params.m_checkLengthCallback(fullLength)) @@ -663,6 +668,7 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, LOG(LINFO, ("----FindPath------wave end (empty)---------------------------", context.forward ? "forward" : "backward")); }; + CHECK(!mtx.has_value(), ("Mutex should be destroyed.")); std::atomic fwdIsEmpty; std::atomic bwdIsEmpty; diff --git a/routing/index_router.cpp b/routing/index_router.cpp index d444c4b1d3c..a3583c3e23d 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -412,7 +412,8 @@ RouterResultCode IndexRouter::CalculateRoute(Checkpoints const & checkpoints, { LOG(LINFO, ("---------------------", twoThreadsReady ? "Two threads" : "One threads", "---------------------")); // TODO |twoThreadsReady| is passed to DoCalculateRoute(), CalculateSubroute() and - // to CalculateSubrouteJointsMode() for test purposes only. + // to CalculateSubrouteJointsMode() for test purposes only. It should be removed in + // these methods. lastReturn = DoCalculateRoute(checkpoints, startDirection, delegate, twoThreadsReady, route); LOG(LINFO, ("---------------------", twoThreadsReady ? "Two threads" : "One threads", "END---------------------")); } From e2f0b0a032c40dba8c5fce092edd0190b3929750 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Sun, 8 Nov 2020 05:39:18 +0300 Subject: [PATCH 17/39] Implementing params.m_onVisitedVertexCallback callback for two two thread version. --- routing/base/astar_algorithm.hpp | 23 ++++++++++++--------- routing/junction_visitor.hpp | 28 +++++++++++++++++++++----- routing/single_vehicle_world_graph.cpp | 3 ++- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index 9073f33a545..b63bb05429a 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,8 @@ namespace astar template struct DefaultVisitor { - void operator()(Vertex const & /* from */, Vertex const & /* to */, bool /* isOutgoing */) const {}; + void operator()(Vertex const & /* from */, Vertex const & /* to */, bool /* isOutgoing */, + std::optional & /* m */) const {}; }; template @@ -504,6 +506,7 @@ AStarAlgorithm::FindPath(P & params, RoutingResult dummy; auto visitVertex = [&](Vertex const & vertex) { if (periodicCancellable.IsCancelled()) { @@ -511,7 +514,7 @@ AStarAlgorithm::FindPath(P & params, RoutingResult::FindPathBidirectional(bool useTwoThreads, // @TODO The multithreading code below in wave lambda is used more or less the same line // as one thread bidirectional version. Consider put in some functions and call them for // one thread and two thread versions. - auto wave = [&epsilon](BidirectionalStepContext & context, std::atomic & queueIsEmpty, + auto wave = [&epsilon, ¶ms](BidirectionalStepContext & context, std::atomic & queueIsEmpty, BidirectionalStepContext & oppositeContext, std::atomic & oppositeQueueIsEmpty) { LOG(LINFO, ("---FindPath-------wave---------------------------", @@ -617,8 +620,10 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, if (context.ExistsStateWithBetterDistance(stateV)) continue; - // @TODO This call should be synchronized in two thread case. - // params.m_onVisitedVertexCallback(stateV.vertex, context.forward ? context.finalVertex : context.startVertex); + params.m_onVisitedVertexCallback(stateV.vertex, + context.forward ? context.finalVertex : context.startVertex, + context.forward, context.mtx); + context.GetAdjacencyList(stateV, adj); auto const & pV = stateV.heuristic; for (auto const & edge : adj) @@ -668,7 +673,6 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, LOG(LINFO, ("----FindPath------wave end (empty)---------------------------", context.forward ? "forward" : "backward")); }; - CHECK(!mtx.has_value(), ("Mutex should be destroyed.")); std::atomic fwdIsEmpty; std::atomic bwdIsEmpty; @@ -686,8 +690,8 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, LOG(LINFO, ("-------FindPath-------Finished-----------------")); ////////////////////////////////////////////////////////////////////////////////////////////////// mtx.reset(); - CHECK(!mtx.has_value(), ()); } + 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 @@ -757,7 +761,7 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, // TODO. |forward| may be false or true in case of two thread routing. params.m_onVisitedVertexCallback(stateV.vertex, - cur->forward ? cur->finalVertex : cur->startVertex, true /* forward */); + cur->forward ? cur->finalVertex : cur->startVertex, true /* forward */, mtx); cur->GetAdjacencyList(stateV, adj); auto const & pV = stateV.heuristic; @@ -857,6 +861,7 @@ AStarAlgorithm::AdjustRoute(P & params, Context context(graph); PeriodicPollCancellable periodicCancellable(params.m_cancellable); + std::optional dummy; auto visitVertex = [&](Vertex const & vertex) { if (periodicCancellable.IsCancelled()) @@ -865,7 +870,7 @@ AStarAlgorithm::AdjustRoute(P & params, return false; } - params.m_onVisitedVertexCallback(startVertex, vertex, true /* forward */); + params.m_onVisitedVertexCallback(startVertex, vertex, true /* forward */, dummy); auto it = remainingDistances.find(vertex); if (it != remainingDistances.cend()) diff --git a/routing/junction_visitor.hpp b/routing/junction_visitor.hpp index afa84fb55e3..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,21 +31,25 @@ class JunctionVisitor m_lastProgressPercent = progress->GetLastPercent(); } - void operator()(Vertex const & from, Vertex const & to, bool isOutgoing) + void operator()(Vertex const & from, Vertex const & to, bool isOutgoing, + std::optional & m) { - // @TODO This method may call from different threads. All the data should be protected. - ++m_visitCounter; - if (m_visitCounter % m_visitPeriod != 0) + if (!IncrementCounterAndCheckPeriod(isOutgoing ? m_visitCounter : m_visitCounterBwd)) return; + 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 */, isOutgoing); auto const currentPercent = progress->UpdateProgress(pointFrom, pointTo); if (currentPercent - m_lastProgressPercent > kProgressInterval) { @@ -51,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/single_vehicle_world_graph.cpp b/routing/single_vehicle_world_graph.cpp index 973584b594f..0e0a30ac181 100644 --- a/routing/single_vehicle_world_graph.cpp +++ b/routing/single_vehicle_world_graph.cpp @@ -205,7 +205,8 @@ vector SingleVehicleWorldGraph::GetSpeedCamInfo(Segme return m_loader->GetSpeedCameraInfo(segment); } -RoadGeometry const & SingleVehicleWorldGraph::GetRoadGeometry(NumMwmId mwmId, uint32_t featureId, bool isOutgoing) +RoadGeometry const & SingleVehicleWorldGraph::GetRoadGeometry(NumMwmId mwmId, uint32_t featureId, + bool isOutgoing) { return m_loader->GetGeometry(mwmId).GetRoad(featureId, isOutgoing); } From 56dfc021ac2d5d747afa70b60be18c73d3aa6494 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Mon, 9 Nov 2020 07:25:49 +0300 Subject: [PATCH 18/39] Calling params.m_checkLengthCallback from two threads. --- routing/base/astar_algorithm.hpp | 22 +++++++++++++--------- routing/index_graph_starter.hpp | 3 +++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index b63bb05429a..aefe58d052e 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -214,6 +214,14 @@ class AStarAlgorithm template Result FindPath(P & params, RoutingResult & result) const; + /// \brief Finds path on |params.m_graph| using bidirectional A* algorithm. + /// \param useTwoThreads if |useTwoThreads| is equal to false one thread version is used. + /// It's worth using one thread version if there's only one core available. + /// if |useTwoThreads| is equal to 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(bool useTwoThreads, P & params, RoutingResult & result) const; @@ -643,11 +651,9 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, stateW.distance = stateV.distance + std::max(reducedWeight, kZeroDistance); - // @TODO This call should be synchronized in two thread case. - // auto const fullLength = weight + stateV.distance + context.pS - pV; - - // if (!params.m_checkLengthCallback(fullLength)) - // continue; + auto const fullLength = weight + stateV.distance + context.pS - pV; + if (!params.m_checkLengthCallback(fullLength, context.forward)) + continue; if (context.ExistsStateWithBetterDistance(stateW, epsilon)) continue; @@ -701,7 +707,6 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, BidirectionalStepContext * nxt = &backward; auto const getResult = [&]() { - // TODO. |forward| may be false or true in case of two thread routing. if (!params.m_checkLengthCallback(bestPathRealLength, true /* forward */)) return Result::NoPath; @@ -759,9 +764,9 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, if (cur->ExistsStateWithBetterDistance(stateV)) continue; - // TODO. |forward| may be false or true in case of two thread routing. params.m_onVisitedVertexCallback(stateV.vertex, - cur->forward ? cur->finalVertex : cur->startVertex, true /* forward */, mtx); + cur->forward ? cur->finalVertex : cur->startVertex, + true /* forward */, mtx); cur->GetAdjacencyList(stateV, adj); auto const & pV = stateV.heuristic; @@ -782,7 +787,6 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, stateW.distance = stateV.distance + std::max(reducedWeight, kZeroDistance); auto const fullLength = weight + stateV.distance + cur->pS - pV; - // TODO. |forward| may be false or true in case of two thread routing. if (!params.m_checkLengthCallback(fullLength, true /* forward */)) continue; diff --git a/routing/index_graph_starter.hpp b/routing/index_graph_starter.hpp index 73786bb1268..5f6ec1612db 100644 --- a/routing/index_graph_starter.hpp +++ b/routing/index_graph_starter.hpp @@ -95,6 +95,9 @@ class IndexGraphStarter : public AStarGraph const & parentVertexData, From 596ec08cae8c09ca059690e3de90482854715392 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Mon, 9 Nov 2020 14:38:34 +0300 Subject: [PATCH 19/39] Fixes to pass integration tests without checks and using one flag in two-threads bidirectional a star instead of two flags. --- routing/base/astar_algorithm.hpp | 20 +++++++++----------- routing/guides_connections.hpp | 5 +++++ routing/index_router.cpp | 7 ++++--- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index aefe58d052e..38e93b2f67e 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -611,14 +611,14 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, // @TODO The multithreading code below in wave lambda is used more or less the same line // as one thread bidirectional version. Consider put in some functions and call them for // one thread and two thread versions. - auto wave = [&epsilon, ¶ms](BidirectionalStepContext & context, std::atomic & queueIsEmpty, + auto wave = [&epsilon, ¶ms](BidirectionalStepContext & context, BidirectionalStepContext & oppositeContext, - std::atomic & oppositeQueueIsEmpty) { + std::atomic & exit) { LOG(LINFO, ("---FindPath-------wave---------------------------", context.forward ? "forward" : "backward")); size_t i = 0; std::vector adj; - while (!context.queue.empty() && !oppositeQueueIsEmpty.load()) + 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. @@ -666,6 +666,7 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, { LOG(LINFO, ("---FindPath-------wave end---------------------------", context.forward ? "forward" : "backward", stateW.vertex, i)); + exit.store(true); return; // The waves are intersected. } @@ -674,27 +675,24 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, } } - if (context.queue.empty()) - queueIsEmpty.store(true); + exit.store(true); LOG(LINFO, ("----FindPath------wave end (empty)---------------------------", context.forward ? "forward" : "backward")); }; - std::atomic fwdIsEmpty; - std::atomic bwdIsEmpty; - + std::atomic shouldExit; PeriodicPollCancellable periodicCancellable(params.m_cancellable); // Starting two wave in two threads. { base::ScopedTimerWithLog timer("Wave"); auto backwardWave = std::async(std::launch::async, wave, std::ref(backward), - std::ref(bwdIsEmpty), std::ref(forward), std::ref(fwdIsEmpty)); - wave(forward, fwdIsEmpty, backward, bwdIsEmpty); + std::ref(forward), std::ref(shouldExit)); + wave(forward, backward, shouldExit); backwardWave.get(); } LOG(LINFO, ("-------FindPath-------Finished-----------------")); - ////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////// mtx.reset(); } CHECK(!mtx.has_value(), ("Mutex should be destroyed.")); 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_router.cpp b/routing/index_router.cpp index a3583c3e23d..9ba1054b656 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -414,6 +414,7 @@ RouterResultCode IndexRouter::CalculateRoute(Checkpoints const & checkpoints, // TODO |twoThreadsReady| is passed to DoCalculateRoute(), CalculateSubroute() and // to CalculateSubrouteJointsMode() for test purposes only. It should be removed in // these methods. + m_guides.Clear(); lastReturn = DoCalculateRoute(checkpoints, startDirection, delegate, twoThreadsReady, route); LOG(LINFO, ("---------------------", twoThreadsReady ? "Two threads" : "One threads", "END---------------------")); } @@ -844,7 +845,7 @@ RouterResultCode IndexRouter::CalculateSubrouteNoLeapsMode( RoutingResult routingResult; set const mwmIds = starter.GetMwms(); RouterResultCode const result = - FindPath(false /* useTwoThreads */, params, mwmIds, routingResult, + FindPath(starter.IsTwoThreadsReady(), params, mwmIds, routingResult, WorldGraphMode::NoLeaps); if (result != RouterResultCode::NoError) @@ -880,7 +881,7 @@ RouterResultCode IndexRouter::CalculateSubrouteLeapsOnlyMode( RoutingResult routingResult; RouterResultCode const result = - FindPath(false /* useTwoThreads */, params, {} /* mwmIds */, + FindPath(leapsGraph.IsTwoThreadsReady(), params, {} /* mwmIds */, routingResult, WorldGraphMode::LeapsOnly); progress->PushAndDropLastSubProgress(); @@ -1369,7 +1370,7 @@ RouterResultCode IndexRouter::ProcessLeapsJoints(vector const & input, nullptr /* prevRoute */, delegate.GetCancellable(), move(visitor), AStarLengthChecker(starter)); - resultCode = FindPath(true /* useTwoThreads */, params, mwmIds, + resultCode = FindPath(jointStarter.IsTwoThreadsReady(), params, mwmIds, routingResult, mode); return resultCode; }; From 7a8c0820158d54450a2ad83ba76ba22f5987c242 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Mon, 9 Nov 2020 15:15:02 +0300 Subject: [PATCH 20/39] Removing useTwoThreads parameter. This information is passed in starter anyway. --- routing/base/astar_algorithm.hpp | 23 +++++++------------ routing/index_router.cpp | 19 +++++++-------- routing/index_router.hpp | 8 +++---- .../routing_tests/astar_algorithm_test.cpp | 4 ++-- routing/routing_tests/index_graph_tools.cpp | 6 ++--- routing/routing_tests/routing_algorithm.cpp | 2 +- 6 files changed, 25 insertions(+), 37 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index 38e93b2f67e..78b4c2bfcb6 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -215,15 +215,17 @@ class AStarAlgorithm Result FindPath(P & params, RoutingResult & result) const; /// \brief Finds path on |params.m_graph| using bidirectional A* algorithm. - /// \param useTwoThreads if |useTwoThreads| is equal to false one thread version is used. + /// \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 |useTwoThreads| is equal to true two thread version is used. If the decision is made to - /// use two thread version it should be taken into account: + /// 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: + /// 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(bool useTwoThreads, P & params, RoutingResult & result) const; + Result FindPathBidirectional(P & params, RoutingResult & result) const; // Adjust route to the previous one. // Expects |params.m_checkLengthCallback| to check wave propagation limit. @@ -568,7 +570,7 @@ AStarAlgorithm::FindPath(P & params, RoutingResult template typename AStarAlgorithm::Result -AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, P & params, +AStarAlgorithm::FindPathBidirectional(P & params, RoutingResult & result) const { auto const epsilon = params.m_weightEpsilon; @@ -576,17 +578,8 @@ AStarAlgorithm::FindPathBidirectional(bool useTwoThreads, auto const & finalVertex = params.m_finalVertex; auto const & startVertex = params.m_startVertex; + auto const useTwoThreads = graph.IsTwoThreadsReady(); std::optional mtx; - if (useTwoThreads) - { - CHECK(graph.IsTwoThreadsReady(), - ("For two threads routing it's necessary to use two threads ready graph.")); - } - else - { - CHECK(!graph.IsTwoThreadsReady(), - ("Only one thread will be used. You may still use two threads graph but it brings some performance leaks.")); - } BidirectionalStepContext forward(mtx, true /* forward */, startVertex, finalVertex, graph); BidirectionalStepContext backward(mtx, false /* forward */, startVertex, finalVertex, graph); diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 9ba1054b656..b0ce65c4916 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -690,7 +690,7 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, SCOPE_GUARD(eraseProgress, [&progress]() { progress->PushAndDropLastSubProgress(); }); auto const result = CalculateSubroute(checkpoints, i, delegate, progress, subrouteStarter, - subroute, twoThreadsReady, m_guides.IsAttached()); + subroute, m_guides.IsAttached()); if (result != RouterResultCode::NoError) return result; @@ -771,7 +771,6 @@ RouterResultCode IndexRouter::CalculateSubroute(Checkpoints const & checkpoints, shared_ptr const & progress, IndexGraphStarter & starter, vector & subroute, - bool twoThreadsReady, bool guidesActive /* = false */) { CHECK(progress, (checkpoints)); @@ -785,7 +784,7 @@ RouterResultCode IndexRouter::CalculateSubroute(Checkpoints const & checkpoints, switch (mode) { case WorldGraphMode::Joints: - return CalculateSubrouteJointsMode(starter, delegate, progress, twoThreadsReady, subroute); + return CalculateSubrouteJointsMode(starter, delegate, progress, subroute); case WorldGraphMode::NoLeaps: return CalculateSubrouteNoLeapsMode(starter, delegate, progress, subroute); case WorldGraphMode::LeapsOnly: @@ -798,7 +797,7 @@ RouterResultCode IndexRouter::CalculateSubroute(Checkpoints const & checkpoints, RouterResultCode IndexRouter::CalculateSubrouteJointsMode( IndexGraphStarter & starter, RouterDelegate const & delegate, - shared_ptr const & progress, bool twoThreadsReady, vector & subroute) + shared_ptr const & progress, vector & subroute) { using JointsStarter = IndexGraphStarterJoints; JointsStarter jointStarter(starter, starter.GetStartSegment(), starter.GetFinishSegment()); @@ -816,9 +815,8 @@ RouterResultCode IndexRouter::CalculateSubrouteJointsMode( AStarLengthChecker(starter)); RoutingResult routingResult; - RouterResultCode const result = - FindPath(twoThreadsReady, params, - {} /* mwmIds */, routingResult, WorldGraphMode::Joints); + RouterResultCode const result = FindPath(params, + {} /* mwmIds */, routingResult, WorldGraphMode::Joints); if (result != RouterResultCode::NoError) return result; @@ -845,7 +843,7 @@ RouterResultCode IndexRouter::CalculateSubrouteNoLeapsMode( RoutingResult routingResult; set const mwmIds = starter.GetMwms(); RouterResultCode const result = - FindPath(starter.IsTwoThreadsReady(), params, mwmIds, routingResult, + FindPath(params, mwmIds, routingResult, WorldGraphMode::NoLeaps); if (result != RouterResultCode::NoError) @@ -881,7 +879,7 @@ RouterResultCode IndexRouter::CalculateSubrouteLeapsOnlyMode( RoutingResult routingResult; RouterResultCode const result = - FindPath(leapsGraph.IsTwoThreadsReady(), params, {} /* mwmIds */, + FindPath(params, {} /* mwmIds */, routingResult, WorldGraphMode::LeapsOnly); progress->PushAndDropLastSubProgress(); @@ -1370,8 +1368,7 @@ RouterResultCode IndexRouter::ProcessLeapsJoints(vector const & input, nullptr /* prevRoute */, delegate.GetCancellable(), move(visitor), AStarLengthChecker(starter)); - resultCode = FindPath(jointStarter.IsTwoThreadsReady(), params, mwmIds, - routingResult, mode); + resultCode = FindPath(params, mwmIds, routingResult, mode); return resultCode; }; diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 0c4a2098ddc..6cd0381167d 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -104,7 +104,6 @@ class IndexRouter : public IRouter RouterResultCode CalculateSubrouteJointsMode(IndexGraphStarter & starter, RouterDelegate const & delegate, std::shared_ptr const & progress, - bool twoThreadsReady, std::vector & subroute); RouterResultCode CalculateSubrouteNoLeapsMode(IndexGraphStarter & starter, RouterDelegate const & delegate, @@ -124,7 +123,7 @@ class IndexRouter : public IRouter RouterDelegate const & delegate, std::shared_ptr const & progress, IndexGraphStarter & graph, std::vector & subroute, - bool twoThreadsReady, bool guidesActive = false); + bool guidesActive = false); RouterResultCode AdjustRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, @@ -209,15 +208,14 @@ class IndexRouter : public IRouter } template - RouterResultCode FindPath(bool useTwoThreads, AStarParams & params, - std::set const & mwmIds, + RouterResultCode FindPath(AStarParams & params, std::set const & mwmIds, RoutingResult & routingResult, WorldGraphMode mode) const { AStarAlgorithm algorithm; return ConvertTransitResult( mwmIds, ConvertResult( - algorithm.FindPathBidirectional(useTwoThreads, params, routingResult))); + algorithm.FindPathBidirectional(params, routingResult))); } void SetupAlgorithmMode(IndexGraphStarter & starter, bool guidesActive = false) const; diff --git a/routing/routing_tests/astar_algorithm_test.cpp b/routing/routing_tests/astar_algorithm_test.cpp index aca87026ef4..f609a1142f7 100644 --- a/routing/routing_tests/astar_algorithm_test.cpp +++ b/routing/routing_tests/astar_algorithm_test.cpp @@ -32,7 +32,7 @@ void TestAStar(UndirectedGraph & graph, vector const & expectedRoute, actualRoute.m_path.clear(); TEST_EQUAL(Algorithm::Result::OK, - algo.FindPathBidirectional(false /* useTwoThreads */, params, actualRoute), ()); + algo.FindPathBidirectional(params, actualRoute), ()); TEST_EQUAL(expectedRoute, actualRoute.m_path, ()); TEST_ALMOST_EQUAL_ULPS(expectedDistance, actualRoute.m_distance, ()); } @@ -76,7 +76,7 @@ UNIT_TEST(AStarAlgorithm_CheckLength) TEST_EQUAL(result, Algorithm::Result::NoPath, ()); routingResult = {}; - result = algo.FindPathBidirectional(false /* useTwoThreads */, params, routingResult); + result = algo.FindPathBidirectional(params, routingResult); // Best route weight is 23 so we expect to find no route with restriction |weight < 23|. TEST_EQUAL(result, Algorithm::Result::NoPath, ()); } diff --git a/routing/routing_tests/index_graph_tools.cpp b/routing/routing_tests/index_graph_tools.cpp index bcc3b4d4245..e5d405ad81f 100644 --- a/routing/routing_tests/index_graph_tools.cpp +++ b/routing/routing_tests/index_graph_tools.cpp @@ -85,7 +85,7 @@ void NoUTurnRestrictionTest::TestRouteGeom(Segment const & start, Segment const RoutingResult routingResult; auto const resultCode = - algorithm.FindPathBidirectional(false /* useTwoThreads */, params, routingResult); + algorithm.FindPathBidirectional(params, routingResult); TEST_EQUAL(resultCode, expectedRouteResult, ()); for (size_t i = 0; i < routingResult.m_path.size(); ++i) @@ -279,7 +279,7 @@ bool TestIndexGraphTopology::FindPath(Vertex start, Vertex finish, double & path nullptr /* prevRoute */); RoutingResult routingResult; auto const resultCode = - algorithm.FindPathBidirectional(false /* useTwoThreads */, params, routingResult); + algorithm.FindPathBidirectional(params, routingResult); // Check unidirectional AStar returns same result. { @@ -485,7 +485,7 @@ AlgorithmForWorldGraph::Result CalculateRoute(IndexGraphStarter & starter, vecto AStarLengthChecker(starter)); auto const resultCode = - algorithm.FindPathBidirectional(false /* useTwoThreads */, params, routingResult); + algorithm.FindPathBidirectional(params, routingResult); timeSec = routingResult.m_distance.GetWeight(); roadPoints = routingResult.m_path; diff --git a/routing/routing_tests/routing_algorithm.cpp b/routing/routing_tests/routing_algorithm.cpp index a70e7151da0..b0759083970 100644 --- a/routing/routing_tests/routing_algorithm.cpp +++ b/routing/routing_tests/routing_algorithm.cpp @@ -216,7 +216,7 @@ TestAStarBidirectionalAlgo::Result TestAStarBidirectionalAlgo::CalculateRoute( cancellable); // TODO. Add two threads tests. - Algorithm::Result const res = Algorithm().FindPathBidirectional(false /* useTwoThreads */, params, path); + Algorithm::Result const res = Algorithm().FindPathBidirectional(params, path); return Convert(res); } } // namespace routing From 5ccfd716fddaa35067749693c14205f2c97e171a Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Mon, 9 Nov 2020 17:27:17 +0300 Subject: [PATCH 21/39] Removing unnecessary comments. --- routing/base/astar_algorithm.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index 78b4c2bfcb6..2651b033697 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -315,7 +315,6 @@ class AStarAlgorithm // particular routes when debugging turned out to be easier. Weight ConsistentHeuristic(Vertex const & v) const { - // TODO. The last parameter may be false in case of two thread routing. bool const isTwoThreads = IsTwoThreadsReady(); auto const piF = graph.HeuristicCostEstimate(v, finalVertex, isTwoThreads ? forward : true); auto const piR = graph.HeuristicCostEstimate(v, startVertex, isTwoThreads ? forward : true); From 30419ecc5cf89678223ec6c7e8e4e34c8ac1ff40 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Wed, 11 Nov 2020 14:35:34 +0300 Subject: [PATCH 22/39] Implementing getting altitude from two threads, all routing integration tests passed. --- coding/file_reader.hpp | 2 +- generator/generator_tests/altitude_test.cpp | 4 +- indexer/altitude_loader.cpp | 53 +++++++++++++------ indexer/altitude_loader.hpp | 24 +++++++-- routing/features_road_graph.cpp | 9 +++- routing/geometry.cpp | 10 ++-- routing/index_router.cpp | 2 +- routing/index_router.hpp | 2 + .../get_altitude_test.cpp | 7 +-- 9 files changed, 79 insertions(+), 34 deletions(-) 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/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/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 04892b051f3..335761f7797 100644 --- a/routing/geometry.cpp +++ b/routing/geometry.cpp @@ -91,7 +91,7 @@ GeometryLoaderImpl::GeometryLoaderImpl(DataSource const & dataSource, , 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(), ()); @@ -118,13 +118,13 @@ void GeometryLoaderImpl::Load(uint32_t featureId, RoadGeometry & road, bool isOu feature->ParseGeometry(FeatureType::BEST_GEOMETRY); geometry::Altitudes const * altitudes = nullptr; - // TODO Altitude should be refactored to be able to be accessible from two threads. -// if (m_loadAltitudes) -// altitudes = &(m_altitudeLoader.GetAltitudes(featureId, feature->GetPointsCount())); + + if (m_loadAltitudes) + 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 ------------------------------------------------------------------------------ diff --git a/routing/index_router.cpp b/routing/index_router.cpp index b0ce65c4916..dd465d24f3e 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -645,7 +645,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()) { diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 6cd0381167d..6ec90f13776 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -244,6 +244,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/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)); From 338773307f5c92ecf67d7717c4a550dae2a8a515 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Wed, 11 Nov 2020 18:01:06 +0300 Subject: [PATCH 23/39] Review fixes. --- routing/base/astar_algorithm.hpp | 1 - routing/geometry.cpp | 37 +++++++++++++++++--------- routing/geometry.hpp | 5 ++-- routing/index_graph.cpp | 2 +- routing/index_router.hpp | 5 ++++ routing/single_vehicle_world_graph.cpp | 4 +-- routing/world_graph.cpp | 2 +- routing/world_graph.hpp | 14 ++++++++++ 8 files changed, 51 insertions(+), 19 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index 2651b033697..528d4af4b3b 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -220,7 +220,6 @@ class AStarAlgorithm /// 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: - /// 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 diff --git a/routing/geometry.cpp b/routing/geometry.cpp index 335761f7797..d9734e07165 100644 --- a/routing/geometry.cpp +++ b/routing/geometry.cpp @@ -274,18 +274,22 @@ double RoadGeometry::GetRoadLengthM() const // Geometry ---------------------------------------------------------------------------------------- Geometry::Geometry(std::unique_ptr loader, bool twoThreadsReady) : m_loader(move(loader)) - , m_featureIdToRoad( - make_unique(twoThreadsReady ? kRoadsCacheSizeTwoCaches : kRoadsCacheSize, - [this](uint32_t featureId, RoadGeometry & road) { - m_loader->Load(featureId, road, true /* isOutgoing */); - })) - , m_featureIdToRoadBwd(twoThreadsReady - ? make_unique( - kRoadsCacheSizeTwoCaches, - [this](uint32_t featureId, RoadGeometry & road) { - m_loader->Load(featureId, road, false /* isOutgoing */); - }) - : nullptr) + , m_featureIdToRoad(MakeCache(twoThreadsReady ? kRoadsCacheSizeTwoCaches : kRoadsCacheSize, + true /* isOutgoing */)) + , m_featureIdToRoadBwd( + twoThreadsReady ? MakeCache(kRoadsCacheSizeTwoCaches, false /* isOutgoing */) : nullptr) +// , m_featureIdToRoad( +// make_unique(twoThreadsReady ? kRoadsCacheSizeTwoCaches : kRoadsCacheSize, +// [this](uint32_t featureId, RoadGeometry & road) { +// m_loader->Load(featureId, road, true /* isOutgoing */); +// })) +// , m_featureIdToRoadBwd(twoThreadsReady +// ? make_unique( +// kRoadsCacheSizeTwoCaches, +// [this](uint32_t featureId, RoadGeometry & road) { +// m_loader->Load(featureId, road, false /* isOutgoing */); +// }) +// : nullptr) { CHECK(m_loader, ()); } @@ -303,6 +307,15 @@ RoadGeometry const & Geometry::GetRoad(uint32_t featureId, bool isOutgoing) 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, diff --git a/routing/geometry.hpp b/routing/geometry.hpp index b553187bca3..370cbf87084 100644 --- a/routing/geometry.hpp +++ b/routing/geometry.hpp @@ -149,11 +149,12 @@ class Geometry final } private: - bool IsTwoThreadsReady() const { return m_featureIdToRoadBwd != nullptr; } - 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*. diff --git a/routing/index_graph.cpp b/routing/index_graph.cpp index c65fc19357f..a3fe6b5b1eb 100644 --- a/routing/index_graph.cpp +++ b/routing/index_graph.cpp @@ -181,7 +181,7 @@ optional IndexGraph::GetJointEdgeByLastPoint(Segment const & parent, Segment const & firstChild, bool isOutgoing, uint32_t lastPoint) { - // @TODO It should be ready for calling form two threads if IsTwoThreadsReady() returns true. + // @TODO It should be ready for calling from two threads if IsTwoThreadsReady() returns true. vector const possibleChildren = {firstChild}; vector const lastPoints = {lastPoint}; diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 6ec90f13776..93b76f2a34d 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, diff --git a/routing/single_vehicle_world_graph.cpp b/routing/single_vehicle_world_graph.cpp index 0e0a30ac181..88c4407675c 100644 --- a/routing/single_vehicle_world_graph.cpp +++ b/routing/single_vehicle_world_graph.cpp @@ -91,7 +91,7 @@ void SingleVehicleWorldGraph::GetEdgeList( astar::VertexData const & vertexData, bool isOutgoing, bool useRoutingOptions, bool useAccessConditional, vector & edges) { - // @TODO It should be ready for calling form two threads if IsTwoThreadsReady() returns true. + // @TODO It should be ready for calling from two threads if IsTwoThreadsReady() returns true. CHECK_NOT_EQUAL(m_mode, WorldGraphMode::LeapsOnly, ()); ASSERT(m_parentsForSegments.forward && m_parentsForSegments.backward, @@ -214,7 +214,7 @@ RoadGeometry const & SingleVehicleWorldGraph::GetRoadGeometry(NumMwmId mwmId, ui void SingleVehicleWorldGraph::GetTwinsInner(Segment const & segment, bool isOutgoing, vector & twins) { - // @TODO It should be ready for calling form two threads if IsTwoThreadsReady() returns true. + // @TODO It should be ready for calling from two threads if IsTwoThreadsReady() returns true. m_crossMwmGraph->GetTwins(segment, isOutgoing, twins); } diff --git a/routing/world_graph.cpp b/routing/world_graph.cpp index f7a3e07ada8..3c399cc2c60 100644 --- a/routing/world_graph.cpp +++ b/routing/world_graph.cpp @@ -14,7 +14,7 @@ void WorldGraph::GetEdgeList(Segment const & vertex, bool isOutgoing, bool useRo void WorldGraph::GetTwins(Segment const & segment, bool isOutgoing, bool useRoutingOptions, std::vector & edges) { - // @TODO It should be ready for calling form two threads if IsTwoThreadsReady() returns true. + // @TODO It should be ready for calling from two threads if IsTwoThreadsReady() returns true. std::vector twins; GetTwinsInner(segment, isOutgoing, twins); diff --git a/routing/world_graph.hpp b/routing/world_graph.hpp index c1c29c66dc2..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 From 53e2dd4bf6d3e4bbbe012b5a9dbebbc1f489e94d Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Wed, 11 Nov 2020 18:05:52 +0300 Subject: [PATCH 24/39] Review fixes --- routing/geometry.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/routing/geometry.cpp b/routing/geometry.cpp index d9734e07165..6debcb4f5c2 100644 --- a/routing/geometry.cpp +++ b/routing/geometry.cpp @@ -278,18 +278,6 @@ Geometry::Geometry(std::unique_ptr loader, bool twoThreadsReady) true /* isOutgoing */)) , m_featureIdToRoadBwd( twoThreadsReady ? MakeCache(kRoadsCacheSizeTwoCaches, false /* isOutgoing */) : nullptr) -// , m_featureIdToRoad( -// make_unique(twoThreadsReady ? kRoadsCacheSizeTwoCaches : kRoadsCacheSize, -// [this](uint32_t featureId, RoadGeometry & road) { -// m_loader->Load(featureId, road, true /* isOutgoing */); -// })) -// , m_featureIdToRoadBwd(twoThreadsReady -// ? make_unique( -// kRoadsCacheSizeTwoCaches, -// [this](uint32_t featureId, RoadGeometry & road) { -// m_loader->Load(featureId, road, false /* isOutgoing */); -// }) -// : nullptr) { CHECK(m_loader, ()); } From 27c8cde1b3a469d8f035c562ccbbbbf53f332e23 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Thu, 12 Nov 2020 11:17:16 +0300 Subject: [PATCH 25/39] Comment about result of investigation of possible dangling references in IndexGraphLoader. --- routing/index_graph_loader.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/routing/index_graph_loader.cpp b/routing/index_graph_loader.cpp index 231a038875a..063f02fe6e0 100644 --- a/routing/index_graph_loader.cpp +++ b/routing/index_graph_loader.cpp @@ -40,6 +40,14 @@ class IndexGraphLoaderImpl final : public IndexGraphLoader 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; From 9d7b19aaf9444742d730b8edbd745beb5951c77f Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Thu, 12 Nov 2020 16:21:12 +0300 Subject: [PATCH 26/39] Adding synchronization for cross mwm case. --- routing/cross_mwm_graph.cpp | 1 - routing/cross_mwm_index_graph.hpp | 2 ++ routing/index_graph.cpp | 3 +++ routing/index_graph.hpp | 3 +++ routing/single_vehicle_world_graph.cpp | 37 ++++++++++++++++++-------- routing/single_vehicle_world_graph.hpp | 12 +++++++++ routing/world_graph.cpp | 1 - 7 files changed, 46 insertions(+), 13 deletions(-) diff --git a/routing/cross_mwm_graph.cpp b/routing/cross_mwm_graph.cpp index 435f47963f5..51cb0bd25c5 100644 --- a/routing/cross_mwm_graph.cpp +++ b/routing/cross_mwm_graph.cpp @@ -88,7 +88,6 @@ void CrossMwmGraph::DeserializeTransitTransitions(vector const & mwmId void CrossMwmGraph::GetTwins(Segment const & s, bool isOutgoing, vector & twins) { - // @TODO. This method may be called from two threads. It should be ready for it. ASSERT(IsTransition(s, isOutgoing), ("The segment", s, "is not a transition segment for isOutgoing ==", isOutgoing)); // Note. There's an extremely rare case when a segment is ingoing and outgoing at the same time. 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/index_graph.cpp b/routing/index_graph.cpp index a3fe6b5b1eb..be1a4d7cb57 100644 --- a/routing/index_graph.cpp +++ b/routing/index_graph.cpp @@ -320,6 +320,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, diff --git a/routing/index_graph.hpp b/routing/index_graph.hpp index 9737e1c9db2..f32664ce628 100644 --- a/routing/index_graph.hpp +++ b/routing/index_graph.hpp @@ -131,6 +131,9 @@ class IndexGraph final bool IsUTurnAndRestricted(Segment const & parent, Segment const & child, bool isOutgoing) const; + /// \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); diff --git a/routing/single_vehicle_world_graph.cpp b/routing/single_vehicle_world_graph.cpp index 88c4407675c..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]); } @@ -91,7 +108,6 @@ void SingleVehicleWorldGraph::GetEdgeList( astar::VertexData const & vertexData, bool isOutgoing, bool useRoutingOptions, bool useAccessConditional, vector & edges) { - // @TODO It should be ready for calling from two threads if IsTwoThreadsReady() returns true. CHECK_NOT_EQUAL(m_mode, WorldGraphMode::LeapsOnly, ()); ASSERT(m_parentsForSegments.forward && m_parentsForSegments.backward, @@ -214,7 +230,6 @@ RoadGeometry const & SingleVehicleWorldGraph::GetRoadGeometry(NumMwmId mwmId, ui void SingleVehicleWorldGraph::GetTwinsInner(Segment const & segment, bool isOutgoing, vector & twins) { - // @TODO It should be ready for calling from two threads if IsTwoThreadsReady() returns true. m_crossMwmGraph->GetTwins(segment, isOutgoing, twins); } diff --git a/routing/single_vehicle_world_graph.hpp b/routing/single_vehicle_world_graph.hpp index 194e48bf5d8..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, @@ -61,6 +65,9 @@ class SingleVehicleWorldGraph final : public WorldGraph 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, bool isOutgoing, @@ -131,6 +138,11 @@ class SingleVehicleWorldGraph final : public WorldGraph 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/world_graph.cpp b/routing/world_graph.cpp index 3c399cc2c60..6eae0778ff4 100644 --- a/routing/world_graph.cpp +++ b/routing/world_graph.cpp @@ -14,7 +14,6 @@ void WorldGraph::GetEdgeList(Segment const & vertex, bool isOutgoing, bool useRo void WorldGraph::GetTwins(Segment const & segment, bool isOutgoing, bool useRoutingOptions, std::vector & edges) { - // @TODO It should be ready for calling from two threads if IsTwoThreadsReady() returns true. std::vector twins; GetTwinsInner(segment, isOutgoing, twins); From e9e847a1ff2f88a5f429dd65273e4c27f566de6d Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Mon, 16 Nov 2020 07:35:36 +0300 Subject: [PATCH 27/39] Correct variant of switching to one thread step in route building. --- routing/base/astar_algorithm.hpp | 102 ++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index 528d4af4b3b..0fabc9f3684 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -356,7 +356,7 @@ class AStarAlgorithm parent.insert_or_assign(to, from); } - void GetAdjacencyList(State const & state, std::vector & adj) + void GetAdjacencyList(State const & state) { auto const realDistance = state.distance + pS - state.heuristic; astar::VertexData const data(state.vertex, realDistance); @@ -380,8 +380,18 @@ class AStarAlgorithm ska::bytell_hash_map bestDistance; Parents parent; Vertex bestVertex; - Weight pS; + // |adj| is a parameter which is filled by GetAdjacencyList(). |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. + std::vector adj; + std::optional stateV; }; static void ReconstructPath(Vertex const & v, @@ -606,11 +616,11 @@ AStarAlgorithm::FindPathBidirectional(P & params, BidirectionalStepContext & oppositeContext, std::atomic & exit) { LOG(LINFO, ("---FindPath-------wave---------------------------", - context.forward ? "forward" : "backward")); + context.forward ? "forward" : "backward", "queue size:", context.queue.size(), "exit:", exit.load())); size_t i = 0; - std::vector adj; while (!context.queue.empty() && !exit.load()) { +// LOG(LINFO, ("---------", context.forward ? "forward" : "backward", "queue size:", context.queue.size())); // 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(); @@ -623,9 +633,12 @@ AStarAlgorithm::FindPathBidirectional(P & params, context.forward ? context.finalVertex : context.startVertex, context.forward, context.mtx); - context.GetAdjacencyList(stateV, adj); + context.GetAdjacencyList(stateV); auto const & pV = stateV.heuristic; - for (auto const & edge : adj) + std::vector toQueue; + toQueue.reserve(context.adj.size()); + bool ret = false; + for (auto const & edge : context.adj) { State stateW(edge.GetTarget(), kZeroDistance); @@ -650,28 +663,50 @@ AStarAlgorithm::FindPathBidirectional(P & params, continue; stateW.heuristic = pW; - context.UpdateDistance(stateW); - context.UpdateParent(stateW.vertex, stateV.vertex); +// context.UpdateDistance(stateW); +// context.UpdateParent(stateW.vertex, stateV.vertex); ++i; - if (oppositeContext.GetDistance(stateW.vertex)) + if (ret || oppositeContext.GetDistance(stateW.vertex)) { LOG(LINFO, ("---FindPath-------wave end---------------------------", context.forward ? "forward" : "backward", stateW.vertex, i)); - exit.store(true); - return; // The waves are intersected. +// context.queue.push(stateV); + ret = true; +// exit.store(true); +// return; // The waves are intersected. } + toQueue.push_back(stateW); - if (stateW.vertex != (context.forward ? context.finalVertex : context.startVertex)) - context.queue.push(stateW); +// if (stateW.vertex != (context.forward ? context.finalVertex : context.startVertex)) +// context.queue.push(stateW); + } + if (ret) + { +// context.queue.push(stateV); + context.stateV = stateV; + exit.store(true); + LOG(LINFO, ("---FindPath-------wave end---------------------------", + context.forward ? "forward" : "backward", i)); + return; + } + else + { + for (auto const & stateW : toQueue) + { + context.UpdateDistance(stateW); + context.UpdateParent(stateW.vertex, stateV.vertex); + if (stateW.vertex != (context.forward ? context.finalVertex : context.startVertex)) + context.queue.push(stateW); + } } } exit.store(true); LOG(LINFO, ("----FindPath------wave end (empty)---------------------------", - context.forward ? "forward" : "backward")); + context.forward ? "forward" : "backward", i)); }; - std::atomic shouldExit; + std::atomic shouldExit = false; PeriodicPollCancellable periodicCancellable(params.m_cancellable); // Starting two wave in two threads. @@ -682,7 +717,7 @@ AStarAlgorithm::FindPathBidirectional(P & params, wave(forward, backward, shouldExit); backwardWave.get(); } - LOG(LINFO, ("-------FindPath-------Finished-----------------")); + LOG(LINFO, ("-------Two thread part of FindPath-------Finished-----------------")); //////////////////////////////////////////////////////////////////////////////////////////////// mtx.reset(); } @@ -709,16 +744,15 @@ 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()) + while (!(cur->queue.empty() && !cur->stateV) && !(nxt->queue.empty() && !nxt->stateV)) { +// LOG(LINFO, ("---------cur is", cur->forward ? "forward" : "backward", "cur queue size:", cur->queue.size(), "next queue size:", nxt->queue.size())); ++steps; if (periodicCancellable.IsCancelled()) @@ -743,23 +777,33 @@ AStarAlgorithm::FindPathBidirectional(P & params, // several top states in a priority queue may have equal reduced path lengths and // different real path lengths. + LOG(LINFO, ("foundAnyPath == true. curTop + nxtTop =", curTop + nxtTop, "bestPathReducedLength - epsilon =", bestPathReducedLength - epsilon)); 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|. + auto const firstStepAfterTwoThreads = useTwoThreads && cur->stateV; + + State const stateV = firstStepAfterTwoThreads ? *cur->stateV : cur->queue.top(); + if (firstStepAfterTwoThreads) + cur->stateV = std::nullopt; + else + cur->queue.pop(); if (cur->ExistsStateWithBetterDistance(stateV)) continue; + if (!firstStepAfterTwoThreads) + { + params.m_onVisitedVertexCallback(stateV.vertex, + cur->forward ? cur->finalVertex : cur->startVertex, + true /* forward */, mtx); + cur->GetAdjacencyList(stateV); + } - params.m_onVisitedVertexCallback(stateV.vertex, - cur->forward ? cur->finalVertex : cur->startVertex, - true /* forward */, mtx); - - 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); @@ -793,8 +837,10 @@ 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. + auto const connectible = graph.AreWavesConnectible(forwardParents, stateW.vertex, backwardParents); + LOG(LINFO, ("foundAnyPath =", foundAnyPath ? "true" : "false", "bestPathReducedLength =", bestPathReducedLength, "curPathReducedLength =", curPathReducedLength, "connectible =", connectible ? "true" : "false")); if ((!foundAnyPath || bestPathReducedLength > curPathReducedLength) && - graph.AreWavesConnectible(forwardParents, stateW.vertex, backwardParents)) + connectible) { bestPathReducedLength = curPathReducedLength; From c418172f0918cbae15801368b82ba8358edb16de Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Mon, 16 Nov 2020 15:14:05 +0300 Subject: [PATCH 28/39] Refactoring. --- routing/base/astar_algorithm.hpp | 114 +++++++++++++++++-------------- 1 file changed, 62 insertions(+), 52 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index 0fabc9f3684..bcfc71b963f 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -295,6 +295,12 @@ 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(), ()); @@ -356,7 +362,20 @@ class AStarAlgorithm parent.insert_or_assign(to, from); } - void GetAdjacencyList(State const & state) + 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); @@ -366,8 +385,20 @@ class AStarAlgorithm graph.GetIngoingEdgesList(data, adj); } + State GetState(bool takesCachedState) + { + State state = takesCachedState ? *stateV : queue.top(); + if (takesCachedState) + stateV = std::nullopt; + else + queue.pop(); + return state; + } + Parents & GetParents() { return parent; } + Vertex const & GetEndVertex() const { return forward ? finalVertex : startVertex; } + bool IsTwoThreadsReady() const { return mtx.has_value(); } std::optional & mtx; @@ -381,7 +412,7 @@ class AStarAlgorithm Parents parent; Vertex bestVertex; Weight pS; - // |adj| is a parameter which is filled by GetAdjacencyList(). |adj| and |stateV| are necessary + // |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() @@ -599,19 +630,12 @@ AStarAlgorithm::FindPathBidirectional(P & params, auto bestPathReducedLength = kZeroDistance; auto bestPathRealLength = kZeroDistance; - forward.UpdateDistance(State(startVertex, kZeroDistance)); - forward.queue.push(State(startVertex, kZeroDistance, forward.ConsistentHeuristic(startVertex))); - - backward.UpdateDistance(State(finalVertex, kZeroDistance)); - backward.queue.push(State(finalVertex, kZeroDistance, backward.ConsistentHeuristic(finalVertex))); + forward.Init(startVertex); + backward.Init(finalVertex); if (useTwoThreads) { mtx.emplace(); - ////////////////////////////////////////////////////////////////////////////////////////////////// - // @TODO The multithreading code below in wave lambda is used more or less the same line - // as one thread bidirectional version. Consider put in some functions and call them for - // one thread and two thread versions. auto wave = [&epsilon, ¶ms](BidirectionalStepContext & context, BidirectionalStepContext & oppositeContext, std::atomic & exit) { @@ -629,15 +653,21 @@ AStarAlgorithm::FindPathBidirectional(P & params, if (context.ExistsStateWithBetterDistance(stateV)) continue; - params.m_onVisitedVertexCallback(stateV.vertex, - context.forward ? context.finalVertex : context.startVertex, - context.forward, context.mtx); + params.m_onVisitedVertexCallback(stateV.vertex, context.GetEndVertex(), context.forward, context.mtx); - context.GetAdjacencyList(stateV); + 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 ret = false; + bool intersectsWithOtherWave = false; + for (auto const & edge : context.adj) { State stateW(edge.GetTarget(), kZeroDistance); @@ -663,26 +693,15 @@ AStarAlgorithm::FindPathBidirectional(P & params, continue; stateW.heuristic = pW; -// context.UpdateDistance(stateW); -// context.UpdateParent(stateW.vertex, stateV.vertex); ++i; - if (ret || oppositeContext.GetDistance(stateW.vertex)) - { - LOG(LINFO, ("---FindPath-------wave end---------------------------", - context.forward ? "forward" : "backward", stateW.vertex, i)); -// context.queue.push(stateV); - ret = true; -// exit.store(true); -// return; // The waves are intersected. - } - toQueue.push_back(stateW); + if (intersectsWithOtherWave || oppositeContext.GetDistance(stateW.vertex)) + intersectsWithOtherWave = true; -// if (stateW.vertex != (context.forward ? context.finalVertex : context.startVertex)) -// context.queue.push(stateW); + toQueue.push_back(stateW); } - if (ret) + if (intersectsWithOtherWave) { -// context.queue.push(stateV); + // |context.stateV| is set here and |context.adj| is already set. context.stateV = stateV; exit.store(true); LOG(LINFO, ("---FindPath-------wave end---------------------------", @@ -692,12 +711,7 @@ AStarAlgorithm::FindPathBidirectional(P & params, else { for (auto const & stateW : toQueue) - { - context.UpdateDistance(stateW); - context.UpdateParent(stateW.vertex, stateV.vertex); - if (stateW.vertex != (context.forward ? context.finalVertex : context.startVertex)) - context.queue.push(stateW); - } + context.UpdateAndPushIfNotEnd(stateW, stateV); } } @@ -750,6 +764,9 @@ AStarAlgorithm::FindPathBidirectional(P & params, uint32_t steps = 0; PeriodicPollCancellable periodicCancellable(params.m_cancellable); + // One thread step. + // While both of contextses are ready to continue. It means + // each one has to have items in queue or some edges have come for two thread step. while (!(cur->queue.empty() && !cur->stateV) && !(nxt->queue.empty() && !nxt->stateV)) { // LOG(LINFO, ("---------cur is", cur->forward ? "forward" : "backward", "cur queue size:", cur->queue.size(), "next queue size:", nxt->queue.size())); @@ -777,7 +794,7 @@ AStarAlgorithm::FindPathBidirectional(P & params, // several top states in a priority queue may have equal reduced path lengths and // different real path lengths. - LOG(LINFO, ("foundAnyPath == true. curTop + nxtTop =", curTop + nxtTop, "bestPathReducedLength - epsilon =", bestPathReducedLength - epsilon)); +// LOG(LINFO, ("foundAnyPath == true. curTop + nxtTop =", curTop + nxtTop, "bestPathReducedLength - epsilon =", bestPathReducedLength - epsilon)); if (curTop + nxtTop >= bestPathReducedLength - epsilon) return getResult(); } @@ -786,20 +803,14 @@ AStarAlgorithm::FindPathBidirectional(P & params, // The information is kept in |cur->stateV| and |cur->adj|. auto const firstStepAfterTwoThreads = useTwoThreads && cur->stateV; - State const stateV = firstStepAfterTwoThreads ? *cur->stateV : cur->queue.top(); - if (firstStepAfterTwoThreads) - cur->stateV = std::nullopt; - else - cur->queue.pop(); + State const stateV = cur->GetState(firstStepAfterTwoThreads); if (cur->ExistsStateWithBetterDistance(stateV)) continue; if (!firstStepAfterTwoThreads) { - params.m_onVisitedVertexCallback(stateV.vertex, - cur->forward ? cur->finalVertex : cur->startVertex, - true /* forward */, mtx); - cur->GetAdjacencyList(stateV); + params.m_onVisitedVertexCallback(stateV.vertex, cur->GetEndVertex(), true /* forward */, mtx); + cur->FillAdjacencyList(stateV); } auto const & pV = stateV.heuristic; @@ -827,8 +838,7 @@ AStarAlgorithm::FindPathBidirectional(P & params, 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) { @@ -838,7 +848,7 @@ AStarAlgorithm::FindPathBidirectional(P & params, auto const curPathReducedLength = stateW.distance + distW; // No epsilon here: it is ok to overshoot slightly. auto const connectible = graph.AreWavesConnectible(forwardParents, stateW.vertex, backwardParents); - LOG(LINFO, ("foundAnyPath =", foundAnyPath ? "true" : "false", "bestPathReducedLength =", bestPathReducedLength, "curPathReducedLength =", curPathReducedLength, "connectible =", connectible ? "true" : "false")); +// LOG(LINFO, ("foundAnyPath =", foundAnyPath ? "true" : "false", "bestPathReducedLength =", bestPathReducedLength, "curPathReducedLength =", curPathReducedLength, "connectible =", connectible ? "true" : "false")); if ((!foundAnyPath || bestPathReducedLength > curPathReducedLength) && connectible) { @@ -854,7 +864,7 @@ AStarAlgorithm::FindPathBidirectional(P & params, } } - if (stateW.vertex != (cur->forward ? cur->finalVertex : cur->startVertex)) + if (stateW.vertex != cur->GetEndVertex()) cur->queue.push(stateW); } } From c4b8f5dbb304682bec5a3ae7357fcf196098b213 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Mon, 16 Nov 2020 15:52:09 +0300 Subject: [PATCH 29/39] Android thread support. --- routing/base/astar_algorithm.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index bcfc71b963f..97307ae2c37 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -9,11 +9,11 @@ #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 @@ -726,10 +726,10 @@ AStarAlgorithm::FindPathBidirectional(P & params, // Starting two wave in two threads. { base::ScopedTimerWithLog timer("Wave"); - auto backwardWave = std::async(std::launch::async, wave, std::ref(backward), - std::ref(forward), std::ref(shouldExit)); + threads::SimpleThread backwardWave(wave, std::ref(backward), + std::ref(forward), std::ref(shouldExit)); wave(forward, backward, shouldExit); - backwardWave.get(); + backwardWave.join(); } LOG(LINFO, ("-------Two thread part of FindPath-------Finished-----------------")); //////////////////////////////////////////////////////////////////////////////////////////////// From 6a82dc73b343f8b834490ff486d235c92db5ee4b Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Mon, 16 Nov 2020 16:59:28 +0300 Subject: [PATCH 30/39] Refactoring. --- routing/base/astar_algorithm.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index 97307ae2c37..ce53c270701 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -399,6 +399,8 @@ class AStarAlgorithm 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; @@ -765,9 +767,7 @@ AStarAlgorithm::FindPathBidirectional(P & params, PeriodicPollCancellable periodicCancellable(params.m_cancellable); // One thread step. - // While both of contextses are ready to continue. It means - // each one has to have items in queue or some edges have come for two thread step. - while (!(cur->queue.empty() && !cur->stateV) && !(nxt->queue.empty() && !nxt->stateV)) + while (cur->IsNextVertex() && nxt->IsNextVertex()) { // LOG(LINFO, ("---------cur is", cur->forward ? "forward" : "backward", "cur queue size:", cur->queue.size(), "next queue size:", nxt->queue.size())); ++steps; From c940b293de952f632538fe9376c3634e3e9ced1f Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Tue, 17 Nov 2020 09:46:00 +0300 Subject: [PATCH 31/39] FindPathBidirectional refactoring. --- routing/base/astar_algorithm.hpp | 225 ++++++++++++++++--------------- 1 file changed, 118 insertions(+), 107 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index ce53c270701..a76a9dd0c63 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -226,6 +226,7 @@ class AStarAlgorithm template Result FindPathBidirectional(P & params, RoutingResult & result) const; + // Adjust route to the previous one. // Expects |params.m_checkLengthCallback| to check wave propagation limit. template @@ -434,6 +435,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 @@ -636,107 +643,8 @@ AStarAlgorithm::FindPathBidirectional(P & params, backward.Init(finalVertex); if (useTwoThreads) - { - 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()) - { -// LOG(LINFO, ("---------", context.forward ? "forward" : "backward", "queue size:", context.queue.size())); - // 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; + FindPathBidirectionalTwoThreadStep(epsilon, params, forward, backward); - 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); - } - } - - exit.store(true); - LOG(LINFO, ("----FindPath------wave end (empty)---------------------------", - context.forward ? "forward" : "backward", i)); - }; - - std::atomic shouldExit = false; - PeriodicPollCancellable periodicCancellable(params.m_cancellable); - - // Starting two wave in two threads. - { - base::ScopedTimerWithLog timer("Wave"); - 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(); - } CHECK(!mtx.has_value(), ("Mutex should be destroyed.")); // To use the search code both for backward and forward directions @@ -769,7 +677,6 @@ AStarAlgorithm::FindPathBidirectional(P & params, // One thread step. while (cur->IsNextVertex() && nxt->IsNextVertex()) { -// LOG(LINFO, ("---------cur is", cur->forward ? "forward" : "backward", "cur queue size:", cur->queue.size(), "next queue size:", nxt->queue.size())); ++steps; if (periodicCancellable.IsCancelled()) @@ -793,8 +700,6 @@ 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. - -// LOG(LINFO, ("foundAnyPath == true. curTop + nxtTop =", curTop + nxtTop, "bestPathReducedLength - epsilon =", bestPathReducedLength - epsilon)); if (curTop + nxtTop >= bestPathReducedLength - epsilon) return getResult(); } @@ -847,10 +752,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. - auto const connectible = graph.AreWavesConnectible(forwardParents, stateW.vertex, backwardParents); -// LOG(LINFO, ("foundAnyPath =", foundAnyPath ? "true" : "false", "bestPathReducedLength =", bestPathReducedLength, "curPathReducedLength =", curPathReducedLength, "connectible =", connectible ? "true" : "false")); - if ((!foundAnyPath || bestPathReducedLength > curPathReducedLength) && - connectible) + auto const connectible = graph.AreWavesConnectible(forwardParents, stateW.vertex, + backwardParents); + if ((!foundAnyPath || bestPathReducedLength > curPathReducedLength) && connectible) { bestPathReducedLength = curPathReducedLength; @@ -1021,4 +925,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 From f02c9c7c75087cc25184b9a732682b623a7d2d21 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Tue, 17 Nov 2020 11:24:07 +0300 Subject: [PATCH 32/39] Removes todo which is done or duplicated --- routing/index_graph.cpp | 1 - routing/index_router.cpp | 2 -- 2 files changed, 3 deletions(-) diff --git a/routing/index_graph.cpp b/routing/index_graph.cpp index be1a4d7cb57..aa1371c5c27 100644 --- a/routing/index_graph.cpp +++ b/routing/index_graph.cpp @@ -181,7 +181,6 @@ optional IndexGraph::GetJointEdgeByLastPoint(Segment const & parent, Segment const & firstChild, bool isOutgoing, uint32_t lastPoint) { - // @TODO It should be ready for calling from two threads if IsTwoThreadsReady() returns true. vector const possibleChildren = {firstChild}; vector const lastPoints = {lastPoint}; diff --git a/routing/index_router.cpp b/routing/index_router.cpp index dd465d24f3e..58d7b5615a3 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -604,8 +604,6 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, vector startSegments; bool startSegmentIsAlmostCodirectionalDirection = false; - // @TODO |startSegments| and |finishSegments| may be found in two threads with the help of - // |graph| with two threads support. bool const foundStart = FindBestSegments(checkpoints.GetPointFrom(), startDirection, true /* isOutgoing */, *graph, startSegments, startSegmentIsAlmostCodirectionalDirection); From 2f2c227acda22501162a9baca990d677c3b4576a Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Tue, 17 Nov 2020 17:29:30 +0300 Subject: [PATCH 33/39] Fixing for routing integration tests passes in debug. --- routing/base/astar_algorithm.hpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/routing/base/astar_algorithm.hpp b/routing/base/astar_algorithm.hpp index a76a9dd0c63..0265bcf7677 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -304,8 +304,9 @@ class AStarAlgorithm 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)) @@ -386,10 +387,10 @@ class AStarAlgorithm graph.GetIngoingEdgesList(data, adj); } - State GetState(bool takesCachedState) + State TopAndPopState() { - State state = takesCachedState ? *stateV : queue.top(); - if (takesCachedState) + State state = stateV ? *stateV : queue.top(); + if (stateV) stateV = std::nullopt; else queue.pop(); @@ -424,6 +425,8 @@ class AStarAlgorithm // 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; }; @@ -706,9 +709,10 @@ AStarAlgorithm::FindPathBidirectional(P & params, // 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|. - auto const firstStepAfterTwoThreads = useTwoThreads && cur->stateV; + // |cur->stateV| should be checked before call |cur->TopAndPopState()|. + auto const firstStepAfterTwoThreads = cur->stateV != std::nullopt; - State const stateV = cur->GetState(firstStepAfterTwoThreads); + State const stateV = cur->TopAndPopState(); if (cur->ExistsStateWithBetterDistance(stateV)) continue; From 501b1bf4d02c48d86dbc769e64e12823ebe327e6 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Wed, 18 Nov 2020 09:30:11 +0300 Subject: [PATCH 34/39] Renaming isTwoThreadsReady paramter to twoThreadsReady. --- routing/index_graph_loader.cpp | 10 +++++----- routing/index_router.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/routing/index_graph_loader.cpp b/routing/index_graph_loader.cpp index 063f02fe6e0..4ecfc370742 100644 --- a/routing/index_graph_loader.cpp +++ b/routing/index_graph_loader.cpp @@ -34,7 +34,7 @@ using namespace std; class IndexGraphLoaderImpl final : public IndexGraphLoader { public: - IndexGraphLoaderImpl(VehicleType vehicleType, bool loadAltitudes, bool isTwoThreadsReady, + IndexGraphLoaderImpl(VehicleType vehicleType, bool loadAltitudes, bool twoThreadsReady, shared_ptr numMwmIds, shared_ptr vehicleModelFactory, shared_ptr estimator, DataSource & dataSource, @@ -85,7 +85,7 @@ class IndexGraphLoaderImpl final : public IndexGraphLoader }; IndexGraphLoaderImpl::IndexGraphLoaderImpl( - VehicleType vehicleType, bool loadAltitudes, bool isTwoThreadsReady, shared_ptr numMwmIds, + VehicleType vehicleType, bool loadAltitudes, bool twoThreadsReady, shared_ptr numMwmIds, shared_ptr vehicleModelFactory, shared_ptr estimator, DataSource & dataSource, RoutingOptions routingOptions) @@ -95,7 +95,7 @@ IndexGraphLoaderImpl::IndexGraphLoaderImpl( , m_numMwmIds(move(numMwmIds)) , m_vehicleModelFactory(move(vehicleModelFactory)) , m_estimator(move(estimator)) - , m_graphsMtx(isTwoThreadsReady ? std::make_optional() : std::nullopt) + , m_graphsMtx(twoThreadsReady ? std::make_optional() : std::nullopt) , m_avoidRoutingOptions(routingOptions) { CHECK(m_numMwmIds, ()); @@ -274,12 +274,12 @@ namespace routing { // static unique_ptr IndexGraphLoader::Create( - VehicleType vehicleType, bool loadAltitudes, bool isTwoThreadsReady, 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, isTwoThreadsReady, numMwmIds, + return make_unique(vehicleType, loadAltitudes, twoThreadsReady, numMwmIds, vehicleModelFactory, estimator, dataSource, routingOptions); } diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 58d7b5615a3..57acb2775ff 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -905,7 +905,7 @@ RouterResultCode IndexRouter::AdjustRoute(Checkpoints const & checkpoints, { base::Timer timer; TrafficStash::Guard guard(m_trafficStash); - auto graph = MakeWorldGraph(false /* isTwoThreadsReady */); + auto graph = MakeWorldGraph(false /* twoThreadsReady */); graph->SetMode(WorldGraphMode::NoLeaps); vector startSegments; From e4762037312a17d2857706694699d1ecd3a845a2 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Wed, 18 Nov 2020 13:35:50 +0300 Subject: [PATCH 35/39] Using useTwoThreads instead of twoThreadsReady in some cases. And passing useTwoThreads for testing. --- routing/async_router.cpp | 2 +- routing/base/astar_algorithm.hpp | 5 +++-- routing/index_router.cpp | 20 ++++--------------- routing/index_router.hpp | 7 ++++--- routing/router.hpp | 5 +++-- routing/routes_builder/routes_builder.cpp | 3 ++- routing/routing_benchmarks/helpers.cpp | 6 +++--- .../routing_test_tools.cpp | 8 +++++--- .../routing_test_tools.hpp | 2 +- routing/routing_tests/async_router_test.cpp | 7 ++++--- .../routing_tests/routing_session_test.cpp | 10 +++++----- 11 files changed, 35 insertions(+), 40 deletions(-) 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 0265bcf7677..0595f9cd689 100644 --- a/routing/base/astar_algorithm.hpp +++ b/routing/base/astar_algorithm.hpp @@ -222,11 +222,10 @@ class AStarAlgorithm /// 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 + /// 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 @@ -630,6 +629,8 @@ AStarAlgorithm::FindPathBidirectional(P & params, auto const & startVertex = params.m_startVertex; 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); diff --git a/routing/index_router.cpp b/routing/index_router.cpp index 57acb2775ff..450e050ce62 100644 --- a/routing/index_router.cpp +++ b/routing/index_router.cpp @@ -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,19 +407,7 @@ RouterResultCode IndexRouter::CalculateRoute(Checkpoints const & checkpoints, } } - RouterResultCode lastReturn = RouterResultCode::InternalError; - for (auto twoThreadsReady : {false, true, true, false}) - { - LOG(LINFO, ("---------------------", twoThreadsReady ? "Two threads" : "One threads", "---------------------")); - // TODO |twoThreadsReady| is passed to DoCalculateRoute(), CalculateSubroute() and - // to CalculateSubrouteJointsMode() for test purposes only. It should be removed in - // these methods. - m_guides.Clear(); - lastReturn = DoCalculateRoute(checkpoints, startDirection, delegate, twoThreadsReady, route); - LOG(LINFO, ("---------------------", twoThreadsReady ? "Two threads" : "One threads", "END---------------------")); - } -// return DoCalculateRoute(checkpoints, startDirection, delegate, false, route); - return lastReturn; + return DoCalculateRoute(checkpoints, startDirection, delegate, useTwoThreads, route); } catch (RootException const & e) { @@ -565,7 +553,7 @@ void IndexRouter::AddGuidesOsmConnectionsToGraphStarter(size_t checkpointIdxFrom RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, RouterDelegate const & delegate, - bool twoThreadsReady, Route & route) + bool useTwoThreads, Route & route) { m_lastRoute.reset(); // MwmId used for guides segments in RedressRoute(). @@ -598,7 +586,7 @@ RouterResultCode IndexRouter::DoCalculateRoute(Checkpoints const & checkpoints, return RouterResultCode::NeedMoreMaps; TrafficStash::Guard guard(m_trafficStash); - unique_ptr graph = MakeWorldGraph(twoThreadsReady); + unique_ptr graph = MakeWorldGraph(useTwoThreads); vector segments; diff --git a/routing/index_router.hpp b/routing/index_router.hpp index 93b76f2a34d..543b462ebb9 100644 --- a/routing/index_router.hpp +++ b/routing/index_router.hpp @@ -97,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; @@ -122,7 +123,7 @@ class IndexRouter : public IRouter RouterResultCode DoCalculateRoute(Checkpoints const & checkpoints, m2::PointD const & startDirection, - RouterDelegate const & delegate, bool twoThreadsReady, + RouterDelegate const & delegate, bool useTwoThreads, Route & route); RouterResultCode CalculateSubroute(Checkpoints const & checkpoints, size_t subrouteIdx, RouterDelegate const & delegate, 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_integration_tests/routing_test_tools.cpp b/routing/routing_integration_tests/routing_test_tools.cpp index eca84aaebd0..d8665ab4dda 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); diff --git a/routing/routing_integration_tests/routing_test_tools.hpp b/routing/routing_integration_tests/routing_test_tools.hpp index e25de08bf7e..fcf09ddb560 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); 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/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 */); From 606657807f9c97bc76183184ecba946a6f9bc6fd Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Wed, 18 Nov 2020 13:59:17 +0300 Subject: [PATCH 36/39] [routing] Unit tests on A* bidirectional in two threads. --- .../routing_tests/astar_algorithm_test.cpp | 23 +++++++++++++++---- routing/routing_tests/routing_algorithm.cpp | 1 - routing/routing_tests/routing_algorithm.hpp | 6 +++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/routing/routing_tests/astar_algorithm_test.cpp b/routing/routing_tests/astar_algorithm_test.cpp index f609a1142f7..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,16 +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, ()); + 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) diff --git a/routing/routing_tests/routing_algorithm.cpp b/routing/routing_tests/routing_algorithm.cpp index b0759083970..283d6bddbb6 100644 --- a/routing/routing_tests/routing_algorithm.cpp +++ b/routing/routing_tests/routing_algorithm.cpp @@ -18,7 +18,6 @@ namespace routing_test { UndirectedGraph::UndirectedGraph() { - CHECK(!IsTwoThreadsReady(), ()); } void UndirectedGraph::AddEdge(Vertex u, Vertex v, Weight w) diff --git a/routing/routing_tests/routing_algorithm.hpp b/routing/routing_tests/routing_algorithm.hpp index 1cb53a89a13..aa02e027401 100644 --- a/routing/routing_tests/routing_algorithm.hpp +++ b/routing/routing_tests/routing_algorithm.hpp @@ -42,14 +42,20 @@ class UndirectedGraph : public AStarGraph void GetOutgoingEdgesList(astar::VertexData const & vertexData, std::vector & adj) 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 From 54cbfb1ef417f644b8a783a6d8f2ac5be4825460 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Thu, 19 Nov 2020 13:21:53 +0300 Subject: [PATCH 37/39] [routing] Adding two routing integration tests on two thread A* bidirectional routing. --- .../pedestrian_route_test.cpp | 10 ++++++++++ routing/routing_integration_tests/route_test.cpp | 11 +++++++++++ .../routing_integration_tests/routing_test_tools.cpp | 4 ++-- .../routing_integration_tests/routing_test_tools.hpp | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) 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 d8665ab4dda..ef8731ed17f 100644 --- a/routing/routing_integration_tests/routing_test_tools.cpp +++ b/routing/routing_integration_tests/routing_test_tools.cpp @@ -236,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 fcf09ddb560..6c8dd3043e1 100644 --- a/routing/routing_integration_tests/routing_test_tools.hpp +++ b/routing/routing_integration_tests/routing_test_tools.hpp @@ -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, From 4442df9f4cbab823b9e29729a02e7253c8ddafeb Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Thu, 19 Nov 2020 13:26:42 +0300 Subject: [PATCH 38/39] Removing unnecessary todo. --- routing/routing_tests/routing_algorithm.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/routing/routing_tests/routing_algorithm.cpp b/routing/routing_tests/routing_algorithm.cpp index 283d6bddbb6..fc5875e4768 100644 --- a/routing/routing_tests/routing_algorithm.cpp +++ b/routing/routing_tests/routing_algorithm.cpp @@ -214,7 +214,6 @@ TestAStarBidirectionalAlgo::Result TestAStarBidirectionalAlgo::CalculateRoute( Algorithm::Params<> params(roadGraph, startPos, finalPos, {} /* prevRoute */, cancellable); - // TODO. Add two threads tests. Algorithm::Result const res = Algorithm().FindPathBidirectional(params, path); return Convert(res); } From f1413d0b0c24638f8bdba254c36cbe65a0d768f4 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Thu, 26 Nov 2020 09:10:58 +0300 Subject: [PATCH 39/39] [routing] Implementing two thread testing for routing consistency tests. --- .../routing_consistency_tests.cpp | 113 +++++++++++++++--- 1 file changed, 99 insertions(+), 14 deletions(-) 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; }