diff --git a/src/linkgraph/linkgraph.h b/src/linkgraph/linkgraph.h index cc64999ebf..2dee34dc16 100644 --- a/src/linkgraph/linkgraph.h +++ b/src/linkgraph/linkgraph.h @@ -12,7 +12,6 @@ #include "../core/pool_type.hpp" #include "../core/smallmap_type.hpp" -#include "../core/smallmatrix_type.hpp" #include "../core/bitmath_func.hpp" #include "../station_base.h" #include "../cargotype.h" diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index 8c13b31748..3503a00171 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -37,7 +37,7 @@ static DateTicks GetLinkGraphJobJoinDateTicks(uint duration_multiplier) /** * Create a link graph job from a link graph. The link graph will be copied so - * that the calculations don't interfer with the normal operations on the + * that the calculations don't interfere with the normal operations on the * original. The job is immediately started. * @param orig Original LinkGraph to be copied. */ @@ -125,13 +125,13 @@ void LinkGraphJob::FinaliseJob() LinkGraph *lg = LinkGraph::Get(ge.link_graph); FlowStatMap &flows = from.Flows(); - this->IterateEdgesFromNode(from, [&](NodeID from_id, NodeID to_id, Edge edge) { - if (edge.Flow() == 0) return; - StationID to = (*this)[to_id].Station(); + for (Edge &edge : from.GetEdges()) { + if (edge.Flow() == 0) continue; + StationID to = (*this)[edge.To()].Station(); Station *st2 = Station::GetIfValid(to); - LinkGraph::ConstEdge lg_edge = lg->GetConstEdge(from_id, to_id); + LinkGraph::ConstEdge lg_edge = lg->GetConstEdge(edge.From(), edge.To()); if (st2 == nullptr || st2->goods[this->Cargo()].link_graph != this->link_graph.index || - st2->goods[this->Cargo()].node != to_id || + st2->goods[this->Cargo()].node != edge.To() || lg_edge.LastUpdate() == INVALID_DATE) { /* Edge has been removed. Delete flows. */ StationIDStack erased = flows.DeleteFlows(to); @@ -143,7 +143,7 @@ void LinkGraphJob::FinaliseJob() /* Edge is fully restricted. */ flows.RestrictFlows(to); } - }); + } /* Swap shares and invalidate ones that are completely deleted. Don't * really delete them as we could then end up with unroutable cargo @@ -197,22 +197,62 @@ void LinkGraphJob::Init() { uint size = this->Size(); this->nodes.resize(size); - this->edges.Resize(size, size); for (uint i = 0; i < size; ++i) { this->nodes[i].Init(this->link_graph[i].Supply()); - EdgeAnnotation *node_edges = this->edges[i]; - for (uint j = 0; j < size; ++j) { - node_edges[j].Init(); - } } -} -/** - * Initialize a linkgraph job edge. - */ -void LinkGraphJob::EdgeAnnotation::Init() -{ - this->flow = 0; + /* Prioritize the fastest route for passengers, mail and express cargo, + * and the shortest route for other classes of cargo. + * In-between stops are punished with a 1 tile or 1 day penalty. */ + const bool express = IsLinkGraphCargoExpress(this->Cargo()); + const uint16 aircraft_link_scale = this->Settings().aircraft_link_scale; + + size_t edge_count = 0; + for (auto &it : this->link_graph.GetEdges()) { + if (it.first.first == it.first.second) continue; + edge_count++; + } + + this->edges.resize(edge_count); + size_t start_idx = 0; + size_t idx = 0; + NodeID last_from = INVALID_NODE; + auto flush = [&]() { + if (last_from == INVALID_NODE) return; + this->nodes[last_from].edges = { this->edges.data() + start_idx, idx - start_idx }; + }; + for (auto &it : this->link_graph.GetEdges()) { + if (it.first.first == it.first.second) continue; + + if (it.first.first != last_from) { + flush(); + last_from = it.first.first; + start_idx = idx; + } + + LinkGraph::ConstEdge edge(it.second); + + auto calculate_distance = [&]() { + return DistanceMaxPlusManhattan((*this)[it.first.first].XY(), (*this)[it.first.second].XY()) + 1; + }; + + uint distance_anno; + if (express) { + /* Compute a default travel time from the distance and an average speed of 1 tile/day. */ + distance_anno = (edge.TravelTime() != 0) ? edge.TravelTime() + DAY_TICKS : calculate_distance() * DAY_TICKS; + } else { + distance_anno = calculate_distance(); + } + + if (edge.LastAircraftUpdate() != INVALID_DATE && aircraft_link_scale > 100) { + distance_anno *= aircraft_link_scale; + distance_anno /= 100; + } + + this->edges[idx].InitEdge(it.first.first, it.first.second, edge.Capacity(), distance_anno); + idx++; + } + flush(); } /** @@ -259,7 +299,7 @@ void Path::Fork(Path *base, uint cap, int free_cap, uint dist) uint Path::AddFlow(uint new_flow, LinkGraphJob &job, uint max_saturation) { if (this->GetParent() != nullptr) { - LinkGraphJob::Edge edge = job[this->GetParent()->node].MakeEdge(job, this->node); + LinkGraphJob::Edge &edge = job[this->GetParent()->node].GetEdgeTo(this->node); if (max_saturation != UINT_MAX) { uint usable_cap = edge.Capacity() * max_saturation / 100; if (usable_cap > edge.Flow()) { diff --git a/src/linkgraph/linkgraphjob.h b/src/linkgraph/linkgraphjob.h index 1102c0e515..9770bdd6c7 100644 --- a/src/linkgraph/linkgraphjob.h +++ b/src/linkgraph/linkgraphjob.h @@ -41,15 +41,78 @@ public: uint unsatisfied_demand = 0; ///< Demand over this edge that hasn't been satisfied yet. }; -private: /** * Annotation for a link graph flow edge. */ - struct EdgeAnnotation { + struct Edge { + private: + NodeID from; ///< From Node. + NodeID to; ///< To Node. + uint capacity; ///< Capacity of the link. + uint distance_anno; ///< Pre-computed distance annotation. + uint flow; ///< Planned flow over this edge. - void Init(); + + public: + /** + * Get edge's from node. + * @return from NodeID. + */ + NodeID From() const { return this->from; } + + /** + * Get edge's to node. + * @return to NodeID. + */ + NodeID To() const { return this->to; } + + /** + * Get edge's capacity. + * @return Capacity. + */ + uint Capacity() const { return this->capacity; } + + /** + * Get edge's distance annotation. + * @return Distance annotation. + */ + uint DistanceAnno() const { return this->distance_anno; } + + /** + * Get the total flow on the edge. + * @return Flow. + */ + uint Flow() const { return this->flow; } + + /** + * Add some flow. + * @param flow Flow to be added. + */ + void AddFlow(uint flow) { this->flow += flow; } + + /** + * Remove some flow. + * @param flow Flow to be removed. + */ + void RemoveFlow(uint flow) + { + dbg_assert(flow <= this->flow); + this->flow -= flow; + } + + void InitEdge(NodeID from, NodeID to, uint capacity, uint distance_anno) + { + this->from = from; + this->to = to; + this->capacity = capacity; + this->distance_anno = distance_anno; + this->flow = 0; + } }; + typedef std::vector EdgeAnnotationVector; + +private: /** * Annotation for a link graph node. */ @@ -59,11 +122,11 @@ private: PathList paths; ///< Paths through this node, sorted so that those with flow == 0 are in the back. FlowStatMap flows; ///< Planned flows to other nodes. span demands; ///< Demand annotations belonging to this node. + span edges; ///< Edges with annotations belonging to this node. void Init(uint supply); }; typedef std::vector NodeAnnotationVector; - typedef SmallMatrix EdgeAnnotationMatrix; friend SaveLoadTable GetLinkGraphJobDesc(); friend upstream_sl::SaveLoadTable upstream_sl::GetLinkGraphJobDesc(); @@ -73,12 +136,13 @@ private: protected: const LinkGraph link_graph; ///< Link graph to by analyzed. Is copied when job is started and mustn't be modified later. + std::shared_ptr group; ///< Job group thread the job is running in or nullptr if it's running in the main thread. const LinkGraphSettings settings; ///< Copy of _settings_game.linkgraph at spawn time. DateTicks join_date_ticks; ///< Date when the job is to be joined. DateTicks start_date_ticks; ///< Date when the job was started. NodeAnnotationVector nodes; ///< Extra node data necessary for link graph calculation. - EdgeAnnotationMatrix edges; ///< Extra edge data necessary for link graph calculation. + EdgeAnnotationVector edges; ///< Edge data necessary for link graph calculation. std::atomic job_completed; ///< Is the job still running. This is accessed by multiple threads and reads may be stale. std::atomic job_aborted; ///< Has the job been aborted. This is accessed by multiple threads and reads may be stale. @@ -93,59 +157,6 @@ public: DynUniformArenaAllocator path_allocator; ///< Arena allocator used for paths - /** - * An annotation-only job edge. Wraps an edge annotation. The - * annotation can be modified. - */ - class AnnoEdge { - private: - EdgeAnnotation &anno; ///< Annotation being wrapped. - public: - /** - * Constructor. - * @param anno Annotation to be wrapped. - */ - AnnoEdge(EdgeAnnotation &anno) : - anno(anno) {} - - /** - * Get the total flow on the edge. - * @return Flow. - */ - uint Flow() const { return this->anno.flow; } - - /** - * Add some flow. - * @param flow Flow to be added. - */ - void AddFlow(uint flow) { this->anno.flow += flow; } - - /** - * Remove some flow. - * @param flow Flow to be removed. - */ - void RemoveFlow(uint flow) - { - assert(flow <= this->anno.flow); - this->anno.flow -= flow; - } - }; - - /** - * A job edge. Wraps a link graph edge and an edge annotation. The - * annotation can be modified, the edge is constant. - */ - class Edge : public LinkGraph::ConstEdge, public AnnoEdge { - public: - /** - * Constructor. - * @param edge Link graph edge to be wrapped. - * @param anno Annotation to be wrapped. - */ - Edge(const LinkGraph::BaseEdge &edge, EdgeAnnotation &anno) : - LinkGraph::ConstEdge(edge), AnnoEdge(anno) {} - }; - /** * Link graph job node. Wraps a constant link graph node and a modifiable * node annotation. @@ -153,7 +164,6 @@ public: class Node : public LinkGraph::ConstNode { private: NodeAnnotation &node_anno; ///< Annotation being wrapped. - EdgeAnnotation *edge_annos; ///< Edge annotations belonging to this node. public: /** @@ -163,21 +173,9 @@ public: */ Node (LinkGraphJob *lgj, NodeID node) : LinkGraph::ConstNode(&lgj->link_graph, node), - node_anno(lgj->nodes[node]), edge_annos(lgj->edges[node]) + node_anno(lgj->nodes[node]) {} - /** - * Retrieve an edge starting at this node. Mind that this returns an - * object, not a reference. - * @param to Remote end of the edge. - * @return Edge between this node and "to". - */ - AnnoEdge operator[](NodeID to) const { return AnnoEdge(this->edge_annos[to]); } - - Edge MakeEdge(const LinkGraph::BaseEdge &base_edge, NodeID to) const { return Edge(base_edge, this->edge_annos[to]); } - - Edge MakeEdge(const LinkGraphJob &lgj, NodeID to) const { return this->MakeEdge(lgj.GetBaseEdge(this->GetNodeID(), to), to); } - /** * Get amount of supply that hasn't been delivered, yet. * @return Undelivered supply. @@ -242,6 +240,21 @@ public: { this->node_anno.demands = demands; } + + Edge &GetEdgeTo(NodeID to) + { + for (Edge &edge : this->node_anno.edges) { + if (edge.To() == to) return edge; + } + + static Edge empty_edge = {}; + return empty_edge; + } + + span GetEdges() + { + return this->node_anno.edges; + } }; /** @@ -346,26 +359,6 @@ public: * @return Link graph. */ inline const LinkGraph &Graph() const { return this->link_graph; } - - const LinkGraph::BaseEdge &GetBaseEdge(NodeID from, NodeID to) const - { - return this->link_graph.GetBaseEdge(from, to); - } - - template - void IterateEdgesFromNode(const Node &from_node, F proc) - { - auto iter = this->link_graph.GetEdges().lower_bound(std::make_pair(from_node.GetNodeID(), (NodeID)0)); - while (iter != this->link_graph.GetEdges().end()) { - NodeID from = iter->first.first; - NodeID to = iter->first.second; - if (from != from_node.GetNodeID()) return; - if (from != to) { - proc(from, to, from_node.MakeEdge(iter->second, to)); - } - ++iter; - } - } }; /** @@ -471,4 +464,11 @@ protected: inline void SetParent(Path *parent) { this->parent_storage = reinterpret_cast(parent) | (this->parent_storage & 1); } }; +inline bool IsLinkGraphCargoExpress(CargoID cargo) +{ + return IsCargoInClass(cargo, CC_PASSENGERS) || + IsCargoInClass(cargo, CC_MAIL) || + IsCargoInClass(cargo, CC_EXPRESS); +} + #endif /* LINKGRAPHJOB_H */ diff --git a/src/linkgraph/linkgraphjob_base.h b/src/linkgraph/linkgraphjob_base.h index b864589d6b..ae71153763 100644 --- a/src/linkgraph/linkgraphjob_base.h +++ b/src/linkgraph/linkgraphjob_base.h @@ -16,7 +16,6 @@ typedef LinkGraphJob::Node Node; typedef LinkGraphJob::Edge Edge; -typedef LinkGraphJob::AnnoEdge AnnoEdge; typedef LinkGraphJob::DemandAnnotation DemandAnnotation; #endif /* LINKGRAPHJOB_BASE_H */ diff --git a/src/linkgraph/mcf.cpp b/src/linkgraph/mcf.cpp index 21219fac85..3d6a6e5aab 100644 --- a/src/linkgraph/mcf.cpp +++ b/src/linkgraph/mcf.cpp @@ -111,11 +111,11 @@ public: */ class GraphEdgeIterator { private: - LinkGraphJob &job; ///< Job being executed - LinkGraph::EdgeMatrix::const_iterator i; ///< Iterator pointing to current edge. - LinkGraph::EdgeMatrix::const_iterator end; ///< Iterator pointing beyond last edge. - NodeID node; ///< Source node - const LinkGraph::BaseEdge *saved; ///< Saved edge + LinkGraphJob &job; ///< Job being executed + const Edge *i; ///< Iterator pointing to current edge. + const Edge *end; ///< Iterator pointing beyond last edge. + NodeID node; ///< Source node + const Edge *saved; ///< Saved edge public: @@ -124,7 +124,7 @@ public: * @param job Job to iterate on. */ GraphEdgeIterator(LinkGraphJob &job) : job(job), - i(LinkGraph::EdgeMatrix::const_iterator()), end(LinkGraph::EdgeMatrix::const_iterator()), node(INVALID_NODE), saved(nullptr) + i(nullptr), end(nullptr), node(INVALID_NODE), saved(nullptr) {} /** @@ -134,8 +134,9 @@ public: */ void SetNode(NodeID source, NodeID node) { - this->i = this->job.Graph().GetEdges().lower_bound(std::make_pair(node, (NodeID)0)); - this->end = this->job.Graph().GetEdges().end(); + Node node_anno = this->job[node]; + this->i = node_anno.GetEdges().begin(); + this->end = node_anno.GetEdges().end(); this->node = node; } @@ -146,17 +147,15 @@ public: NodeID Next() { if (this->i == this->end) return INVALID_NODE; - NodeID from = this->i->first.first; - NodeID to = this->i->first.second; - if (from != this->node) return INVALID_NODE; - this->saved = &(this->i->second); + NodeID to = this->i->To(); + this->saved = this->i; ++this->i; return to; } bool SavedEdge() const { return true; } - const LinkGraph::BaseEdge &GetSavedEdge() { return *(this->saved); } + const Edge &GetSavedEdge() { return *(this->saved); } }; /** @@ -221,7 +220,7 @@ public: bool SavedEdge() const { return false; } - const LinkGraph::BaseEdge &GetSavedEdge() { NOT_REACHED(); } + const Edge &GetSavedEdge() { NOT_REACHED(); } }; /** @@ -301,8 +300,6 @@ void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths) this->job.path_allocator.SetParameters(sizeof(Tannotation), (8192 - 32) / sizeof(Tannotation)); - const uint16 aircraft_link_scale = this->job.Settings().aircraft_link_scale; - for (NodeID node = 0; node < size; ++node) { Tannotation *anno = new (this->job.path_allocator.Allocate()) Tannotation(node, node == source_node); anno->UpdateAnnotation(); @@ -320,39 +317,18 @@ void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector &paths) iter.SetNode(source_node, from); for (NodeID to = iter.Next(); to != INVALID_NODE; to = iter.Next()) { if (to == from) continue; // Not a real edge but a consumption sign. - const LinkGraph::BaseEdge &base_edge = iter.SavedEdge() ? iter.GetSavedEdge() : this->job.GetBaseEdge(from, to); - Edge edge = this->job[from].MakeEdge(base_edge, to); + const Edge &edge = iter.SavedEdge() ? iter.GetSavedEdge() : this->job[from].GetEdgeTo(to); uint capacity = edge.Capacity(); if (this->max_saturation != UINT_MAX) { capacity *= this->max_saturation; capacity /= 100; if (capacity == 0) capacity = 1; } - /* Prioritize the fastest route for passengers, mail and express cargo, - * and the shortest route for other classes of cargo. - * In-between stops are punished with a 1 tile or 1 day penalty. */ - bool express = IsLinkGraphCargoExpress(this->job.Cargo()); - auto calculate_distance = [&]() { - return DistanceMaxPlusManhattan(this->job[from].XY(), this->job[to].XY()) + 1; - }; - - uint distance_anno; - if (express) { - /* Compute a default travel time from the distance and an average speed of 1 tile/day. */ - distance_anno = (edge.TravelTime() != 0) ? edge.TravelTime() + DAY_TICKS : calculate_distance() * DAY_TICKS; - } else { - distance_anno = calculate_distance(); - } - - if (edge.LastAircraftUpdate() != INVALID_DATE && aircraft_link_scale > 100) { - distance_anno *= aircraft_link_scale; - distance_anno /= 100; - } Tannotation *dest = static_cast(paths[to]); - if (dest->IsBetter(source, capacity, capacity - edge.Flow(), distance_anno)) { + if (dest->IsBetter(source, capacity, capacity - edge.Flow(), edge.DistanceAnno())) { if (dest->GetAnnosSetFlag()) annos.erase(AnnoSetItem(dest)); - dest->Fork(source, capacity, capacity - edge.Flow(), distance_anno); + dest->Fork(source, capacity, capacity - edge.Flow(), edge.DistanceAnno()); dest->UpdateAnnotation(); annos.insert(AnnoSetItem(dest)); dest->SetAnnosSetFlag(true); @@ -447,7 +423,7 @@ void MCF1stPass::EliminateCycle(PathVector &path, Path *cycle_begin, uint flow) } } cycle_begin = path[prev]; - AnnoEdge edge = this->job[prev][cycle_begin->GetNode()]; + Edge &edge = this->job[prev].GetEdgeTo(cycle_begin->GetNode()); edge.RemoveFlow(flow); } while (cycle_begin != cycle_end); } diff --git a/src/linkgraph/mcf.h b/src/linkgraph/mcf.h index 06f344e375..17ab216f31 100644 --- a/src/linkgraph/mcf.h +++ b/src/linkgraph/mcf.h @@ -89,11 +89,4 @@ public: virtual ~MCFHandler() {} }; -inline bool IsLinkGraphCargoExpress(CargoID cargo) -{ - return IsCargoInClass(cargo, CC_PASSENGERS) || - IsCargoInClass(cargo, CC_MAIL) || - IsCargoInClass(cargo, CC_EXPRESS); -} - #endif /* MCF_H */