From 90550d9642c7ef25580390894af17a7cd9de43a2 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 28 Sep 2019 04:20:01 +0100 Subject: [PATCH] FlowStatMap: Replace RB-tree with btree-indexed vector --- src/cargopacket.cpp | 8 +- src/linkgraph/flowmapper.cpp | 2 +- src/linkgraph/linkgraphjob.cpp | 17 +-- src/linkgraph/mcf.cpp | 4 +- src/saveload/station_sl.cpp | 10 +- src/script/api/script_stationlist.cpp | 8 +- src/station_base.h | 143 ++++++++++++++++++++++++-- src/station_cmd.cpp | 49 +++++---- src/station_gui.cpp | 10 +- 9 files changed, 199 insertions(+), 52 deletions(-) diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp index f376101b45..2c66975787 100644 --- a/src/cargopacket.cpp +++ b/src/cargopacket.cpp @@ -660,7 +660,7 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID if (flow_it == ge->flows.end()) { cargo_next = INVALID_STATION; } else { - FlowStat new_shares = flow_it->second; + FlowStat new_shares = *flow_it; new_shares.ChangeShare(current_station, INT_MIN); StationIDStack excluded = next_station; while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) { @@ -676,20 +676,20 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID /* Rewrite an invalid source station to some random other one to * avoid keeping the cargo in the vehicle forever. */ if (cp->source == INVALID_STATION && !ge->flows.empty()) { - cp->source = ge->flows.begin()->first; + cp->source = ge->flows.FirstStationID(); } bool restricted = false; FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source)); if (flow_it == ge->flows.end()) { cargo_next = INVALID_STATION; } else { - cargo_next = flow_it->second.GetViaWithRestricted(restricted); + cargo_next = flow_it->GetViaWithRestricted(restricted); } action = VehicleCargoList::ChooseAction(cp, cargo_next, current_station, accepted, next_station); if (restricted && action == MTA_TRANSFER) { /* If the flow is restricted we can't transfer to it. Choose an * unrestricted one instead. */ - cargo_next = flow_it->second.GetVia(); + cargo_next = flow_it->GetVia(); action = VehicleCargoList::ChooseAction(cp, cargo_next, current_station, accepted, next_station); } } diff --git a/src/linkgraph/flowmapper.cpp b/src/linkgraph/flowmapper.cpp index 1aea4a82e6..a36a9b2345 100644 --- a/src/linkgraph/flowmapper.cpp +++ b/src/linkgraph/flowmapper.cpp @@ -57,7 +57,7 @@ void FlowMapper::Run(LinkGraphJob &job) const * LinkGraph::Monthly(). */ uint runtime = (job.StartDateTicks() / DAY_TICKS) - job.LastCompression() + 1; for (FlowStatMap::iterator i = flows.begin(); i != flows.end(); ++i) { - i->second.ScaleToMonthly(runtime); + i->ScaleToMonthly(runtime); } } /* Clear paths. */ diff --git a/src/linkgraph/linkgraphjob.cpp b/src/linkgraph/linkgraphjob.cpp index d2df0fb71c..c6db2adbef 100644 --- a/src/linkgraph/linkgraphjob.cpp +++ b/src/linkgraph/linkgraphjob.cpp @@ -146,27 +146,30 @@ void LinkGraphJob::FinaliseJob() * somewhere. Do delete them and also reroute relevant cargo if * automatic distribution has been turned off for that cargo. */ for (FlowStatMap::iterator it(ge.flows.begin()); it != ge.flows.end();) { - FlowStatMap::iterator new_it = flows.find(it->first); + FlowStatMap::iterator new_it = flows.find(it->GetOrigin()); if (new_it == flows.end()) { if (_settings_game.linkgraph.GetDistributionType(this->Cargo()) != DT_MANUAL) { - it->second.Invalidate(); + it->Invalidate(); ++it; } else { - FlowStat shares(INVALID_STATION, 1); - it->second.SwapShares(shares); - ge.flows.erase(it++); + FlowStat shares(INVALID_STATION, INVALID_STATION, 1); + it->SwapShares(shares); + it = ge.flows.erase(it); for (FlowStat::SharesMap::const_iterator shares_it(shares.GetShares()->begin()); shares_it != shares.GetShares()->end(); ++shares_it) { RerouteCargo(st, this->Cargo(), shares_it->second, st->index); } } } else { - it->second.SwapShares(new_it->second); + it->SwapShares(*new_it); flows.erase(new_it); ++it; } } - ge.flows.insert(flows.begin(), flows.end()); + for (FlowStatMap::iterator it(flows.begin()); it != flows.end(); ++it) { + ge.flows.insert(std::move(*it)); + } + ge.flows.SortStorage(); InvalidateWindowData(WC_STATION_VIEW, st->index, this->Cargo()); } } diff --git a/src/linkgraph/mcf.cpp b/src/linkgraph/mcf.cpp index 8453979ed9..d87b3fd9b2 100644 --- a/src/linkgraph/mcf.cpp +++ b/src/linkgraph/mcf.cpp @@ -187,8 +187,8 @@ public: const FlowStatMap &flows = this->job[node].Flows(); FlowStatMap::const_iterator it = flows.find(this->job[source].Station()); if (it != flows.end()) { - this->it = it->second.GetShares()->begin(); - this->end = it->second.GetShares()->end(); + this->it = it->GetShares()->begin(); + this->end = it->GetShares()->end(); } else { this->it = FlowStat::empty_sharesmap.begin(); this->end = FlowStat::empty_sharesmap.end(); diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index 29e3107daa..559c140e13 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -523,18 +523,18 @@ static void RealSave_STNN(BaseStation *bst) _num_dests = (uint32)st->goods[i].cargo.Packets()->MapSize(); _num_flows = 0; for (FlowStatMap::const_iterator it(st->goods[i].flows.begin()); it != st->goods[i].flows.end(); ++it) { - _num_flows += (uint32)it->second.GetShares()->size(); + _num_flows += (uint32)it->GetShares()->size(); } SlObjectSaveFiltered(&st->goods[i], _filtered_goods_desc.data()); for (FlowStatMap::const_iterator outer_it(st->goods[i].flows.begin()); outer_it != st->goods[i].flows.end(); ++outer_it) { - const FlowStat::SharesMap *shares = outer_it->second.GetShares(); + const FlowStat::SharesMap *shares = outer_it->GetShares(); uint32 sum_shares = 0; FlowSaveLoad flow; - flow.source = outer_it->first; + flow.source = outer_it->GetOrigin(); for (FlowStat::SharesMap::const_iterator inner_it(shares->begin()); inner_it != shares->end(); ++inner_it) { flow.via = inner_it->second; flow.share = inner_it->first - sum_shares; - flow.restricted = inner_it->first > outer_it->second.GetUnrestricted(); + flow.restricted = inner_it->first > outer_it->GetUnrestricted(); sum_shares = inner_it->first; assert(flow.share > 0); @@ -610,7 +610,7 @@ static void Load_STNN() if (!IsSavegameVersionBefore(SLV_187)) flow.restricted = (buffer->ReadByte() != 0); if (fs == nullptr || prev_source != flow.source) { - fs = &(st->goods[i].flows.insert(st->goods[i].flows.end(), std::make_pair(flow.source, FlowStat(flow.via, flow.share, flow.restricted)))->second); + fs = &(*(st->goods[i].flows.insert(st->goods[i].flows.end(), FlowStat(flow.source, flow.via, flow.share, flow.restricted)))); } else { fs->AppendShare(flow.via, flow.share, flow.restricted); } diff --git a/src/script/api/script_stationlist.cpp b/src/script/api/script_stationlist.cpp index 4bbb5fa677..2174ae66b4 100644 --- a/src/script/api/script_stationlist.cpp +++ b/src/script/api/script_stationlist.cpp @@ -195,11 +195,11 @@ void ScriptStationList_CargoPlanned::Add(StationID station_id, CargoID cargo, St FlowStatMap::const_iterator iter = collector.GE()->flows.begin(); FlowStatMap::const_iterator end = collector.GE()->flows.end(); for (; iter != end; ++iter) { - const FlowStat::SharesMap *shares = iter->second.GetShares(); + const FlowStat::SharesMap *shares = iter->GetShares(); uint prev = 0; for (FlowStat::SharesMap::const_iterator flow_iter = shares->begin(); flow_iter != shares->end(); ++flow_iter) { - collector.Update(iter->first, flow_iter->second, flow_iter->first - prev); + collector.Update(iter->GetOrigin(), flow_iter->second, flow_iter->first - prev); prev = flow_iter->first; } } @@ -265,11 +265,11 @@ ScriptStationList_CargoPlannedFromByVia::ScriptStationList_CargoPlannedFromByVia FlowStatMap::const_iterator iter = collector.GE()->flows.find(from); if (iter == collector.GE()->flows.end()) return; - const FlowStat::SharesMap *shares = iter->second.GetShares(); + const FlowStat::SharesMap *shares = iter->GetShares(); uint prev = 0; for (FlowStat::SharesMap::const_iterator flow_iter = shares->begin(); flow_iter != shares->end(); ++flow_iter) { - collector.Update(iter->first, flow_iter->second, flow_iter->first - prev); + collector.Update(iter->GetOrigin(), flow_iter->second, flow_iter->first - prev); prev = flow_iter->first; } } diff --git a/src/station_base.h b/src/station_base.h index 418a21cb93..27907c12c5 100644 --- a/src/station_base.h +++ b/src/station_base.h @@ -24,12 +24,16 @@ #include "bitmap_type.h" #include #include +#include +#include typedef Pool StationPool; extern StationPool _station_pool; static const byte INITIAL_STATION_RATING = 175; +class FlowStatMap; + /** * Flow statistics telling how much flow should be sent along a link. This is * done by creating "flow shares" and using std::map's upper_bound() method to @@ -38,6 +42,7 @@ static const byte INITIAL_STATION_RATING = 175; * mean anything by itself. */ class FlowStat { + friend FlowStatMap; public: typedef btree::btree_map SharesMap; @@ -52,15 +57,17 @@ public: /** * Create a FlowStat with an initial entry. - * @param st Station the initial entry refers to. + * @param origin Origin station for this flow. + * @param via Station the initial entry refers to. * @param flow Amount of flow for the initial entry. * @param restricted If the flow to be added is restricted. */ - inline FlowStat(StationID st, uint flow, bool restricted = false) + inline FlowStat(StationID origin, StationID via, uint flow, bool restricted = false) { assert(flow > 0); - this->shares[flow] = st; + this->shares[flow] = via; this->unrestricted = restricted ? 0 : flow; + this->origin = origin; } /** @@ -148,14 +155,66 @@ public: void Invalidate(); + inline StationID GetOrigin() const + { + return this->origin; + } + private: SharesMap shares; ///< Shares of flow to be sent via specified station (or consumed locally). uint unrestricted; ///< Limit for unrestricted shares. + StationID origin; +}; +static_assert(std::is_nothrow_move_constructible::value, "FlowStat must be nothrow move constructible"); + +template +class FlowStatMapIterator +{ + friend FlowStatMap; + friend FlowStatMapIterator::iterator>; + friend FlowStatMapIterator::const_iterator>; +public: + typedef FlowStat value_type; + typedef cv_value& reference; + typedef cv_value* pointer; + typedef ptrdiff_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + FlowStatMapIterator(cv_container *fsm, cv_index_iter current) : + fsm(fsm), current(current) {} + + FlowStatMapIterator(const FlowStatMapIterator::iterator> &other) : + fsm(other.fsm), current(other.current) {} + + reference operator*() const { return this->fsm->flows_storage[this->current->second]; } + pointer operator->() const { return &(this->fsm->flows_storage[this->current->second]); } + + FlowStatMapIterator& operator++() + { + ++this->current; + return *this; + } + + bool operator==(const FlowStatMapIterator& rhs) const { return this->current == rhs.current; } + bool operator!=(const FlowStatMapIterator& rhs) const { return !(operator==(rhs)); } + +private: + cv_container *fsm; + cv_index_iter current; }; /** Flow descriptions by origin stations. */ -class FlowStatMap : public std::map { +class FlowStatMap { + std::vector flows_storage; + btree::btree_map flows_index; + public: + using iterator = FlowStatMapIterator::iterator>; + using const_iterator = FlowStatMapIterator::const_iterator>; + + friend iterator; + friend const_iterator; + uint GetFlow() const; uint GetFlowVia(StationID via) const; uint GetFlowFrom(StationID from) const; @@ -167,6 +226,78 @@ public: void RestrictFlows(StationID via); void ReleaseFlows(StationID via); void FinalizeLocalConsumption(StationID self); + +private: + btree::btree_map::iterator erase_priv(btree::btree_map::iterator iter) + { + uint16 index = iter->second; + iter = this->flows_index.erase(iter); + if (index != this->flows_storage.size() - 1) { + this->flows_storage[index] = std::move(this->flows_storage.back()); + this->flows_index[this->flows_storage[index].GetOrigin()] = index; + } + this->flows_storage.pop_back(); + return iter; + } + +public: + iterator begin() { return iterator(this, this->flows_index.begin()); } + const_iterator begin() const { return const_iterator(this, this->flows_index.begin()); } + iterator end() { return iterator(this, this->flows_index.end()); } + const_iterator end() const { return const_iterator(this, this->flows_index.end()); } + + iterator find(StationID from) + { + return iterator(this, this->flows_index.find(from)); + } + const_iterator find(StationID from) const + { + return const_iterator(this, this->flows_index.find(from)); + } + + bool empty() const + { + return this->flows_index.empty(); + } + + void erase(StationID st) + { + auto iter = this->flows_index.find(st); + if (iter != this->flows_index.end()) { + this->erase_priv(iter); + } + } + + iterator erase(iterator iter) + { + return iterator(this, this->erase_priv(iter.current)); + } + + std::pair insert(FlowStat flow_stat) + { + StationID st = flow_stat.GetOrigin(); + auto res = this->flows_index.insert(std::pair(st, this->flows_storage.size())); + if (res.second) { + this->flows_storage.push_back(std::move(flow_stat)); + } + return std::make_pair(iterator(this, res.first), res.second); + } + + iterator insert(iterator hint, FlowStat flow_stat) + { + auto res = this->flows_index.insert(hint.current, std::pair(flow_stat.GetOrigin(), this->flows_storage.size())); + if (res->second == this->flows_storage.size()) { + this->flows_storage.push_back(std::move(flow_stat)); + } + return iterator(this, res); + } + + StationID FirstStationID() const + { + return this->flows_index.begin()->first; + } + + void SortStorage(); }; /** @@ -291,7 +422,7 @@ struct GoodsEntry { inline StationID GetVia(StationID source) const { FlowStatMap::const_iterator flow_it(this->flows.find(source)); - return flow_it != this->flows.end() ? flow_it->second.GetVia() : INVALID_STATION; + return flow_it != this->flows.end() ? flow_it->GetVia() : INVALID_STATION; } /** @@ -305,7 +436,7 @@ struct GoodsEntry { inline StationID GetVia(StationID source, StationID excluded, StationID excluded2 = INVALID_STATION) const { FlowStatMap::const_iterator flow_it(this->flows.find(source)); - return flow_it != this->flows.end() ? flow_it->second.GetVia(excluded, excluded2) : INVALID_STATION; + return flow_it != this->flows.end() ? flow_it->GetVia(excluded, excluded2) : INVALID_STATION; } }; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index fcf431342d..01770a6a20 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -4866,10 +4866,10 @@ void FlowStatMap::AddFlow(StationID origin, StationID via, uint flow) { FlowStatMap::iterator origin_it = this->find(origin); if (origin_it == this->end()) { - this->insert(std::make_pair(origin, FlowStat(via, flow))); + this->insert(FlowStat(origin, via, flow)); } else { - origin_it->second.ChangeShare(via, flow); - assert(!origin_it->second.GetShares()->empty()); + origin_it->ChangeShare(via, flow); + assert(!origin_it->GetShares()->empty()); } } @@ -4885,13 +4885,13 @@ void FlowStatMap::PassOnFlow(StationID origin, StationID via, uint flow) { FlowStatMap::iterator prev_it = this->find(origin); if (prev_it == this->end()) { - FlowStat fs(via, flow); + FlowStat fs(origin, via, flow); fs.AppendShare(INVALID_STATION, flow); - this->insert(std::make_pair(origin, fs)); + this->insert(std::move(fs)); } else { - prev_it->second.ChangeShare(via, flow); - prev_it->second.ChangeShare(INVALID_STATION, flow); - assert(!prev_it->second.GetShares()->empty()); + prev_it->ChangeShare(via, flow); + prev_it->ChangeShare(INVALID_STATION, flow); + assert(!prev_it->GetShares()->empty()); } } @@ -4902,7 +4902,7 @@ void FlowStatMap::PassOnFlow(StationID origin, StationID via, uint flow) void FlowStatMap::FinalizeLocalConsumption(StationID self) { for (FlowStatMap::iterator i = this->begin(); i != this->end(); ++i) { - FlowStat &fs = i->second; + FlowStat &fs = *i; uint local = fs.GetShare(INVALID_STATION); if (local > INT_MAX) { // make sure it fits in an int fs.ChangeShare(self, -INT_MAX); @@ -4928,11 +4928,11 @@ StationIDStack FlowStatMap::DeleteFlows(StationID via) { StationIDStack ret; for (FlowStatMap::iterator f_it = this->begin(); f_it != this->end();) { - FlowStat &s_flows = f_it->second; + FlowStat &s_flows = *f_it; s_flows.ChangeShare(via, INT_MIN); if (s_flows.GetShares()->empty()) { - ret.Push(f_it->first); - this->erase(f_it++); + ret.Push(f_it->GetOrigin()); + f_it = this->erase(f_it); } else { ++f_it; } @@ -4947,7 +4947,7 @@ StationIDStack FlowStatMap::DeleteFlows(StationID via) void FlowStatMap::RestrictFlows(StationID via) { for (FlowStatMap::iterator it = this->begin(); it != this->end(); ++it) { - it->second.RestrictShare(via); + it->RestrictShare(via); } } @@ -4958,7 +4958,7 @@ void FlowStatMap::RestrictFlows(StationID via) void FlowStatMap::ReleaseFlows(StationID via) { for (FlowStatMap::iterator it = this->begin(); it != this->end(); ++it) { - it->second.ReleaseShare(via); + it->ReleaseShare(via); } } @@ -4970,7 +4970,7 @@ uint FlowStatMap::GetFlow() const { uint ret = 0; for (FlowStatMap::const_iterator i = this->begin(); i != this->end(); ++i) { - ret += (--(i->second.GetShares()->end()))->first; + ret += (--(i->GetShares()->end()))->first; } return ret; } @@ -4984,7 +4984,7 @@ uint FlowStatMap::GetFlowVia(StationID via) const { uint ret = 0; for (FlowStatMap::const_iterator i = this->begin(); i != this->end(); ++i) { - ret += i->second.GetShare(via); + ret += i->GetShare(via); } return ret; } @@ -4998,7 +4998,7 @@ uint FlowStatMap::GetFlowFrom(StationID from) const { FlowStatMap::const_iterator i = this->find(from); if (i == this->end()) return 0; - return (--(i->second.GetShares()->end()))->first; + return (--(i->GetShares()->end()))->first; } /** @@ -5011,7 +5011,20 @@ uint FlowStatMap::GetFlowFromVia(StationID from, StationID via) const { FlowStatMap::const_iterator i = this->find(from); if (i == this->end()) return 0; - return i->second.GetShare(via); + return i->GetShare(via); +} + +void FlowStatMap::SortStorage() +{ + assert(this->flows_storage.size() == this->flows_index.size()); + std::sort(this->flows_storage.begin(), this->flows_storage.end(), [](const FlowStat &a, const FlowStat &b) -> bool { + return a.origin < b.origin; + }); + uint16 index = 0; + for (auto &it : this->flows_index) { + it.second = index; + index++; + } } extern const TileTypeProcs _tile_type_station_procs = { diff --git a/src/station_gui.cpp b/src/station_gui.cpp index 804e0cb033..50f3cd75cd 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -1513,9 +1513,9 @@ struct StationViewWindow : public Window { const FlowStatMap &flows = st->goods[i].flows; for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) { - StationID from = it->first; + StationID from = it->GetOrigin(); CargoDataEntry *source_entry = cargo_entry->InsertOrRetrieve(from); - const FlowStat::SharesMap *shares = it->second.GetShares(); + const FlowStat::SharesMap *shares = it->GetShares(); uint32 prev_count = 0; for (FlowStat::SharesMap::const_iterator flow_it = shares->begin(); flow_it != shares->end(); ++flow_it) { StationID via = flow_it->second; @@ -1546,7 +1546,7 @@ struct StationViewWindow : public Window { const FlowStatMap &flowmap = Station::Get(next)->goods[cargo].flows; FlowStatMap::const_iterator map_it = flowmap.find(source); if (map_it != flowmap.end()) { - const FlowStat::SharesMap *shares = map_it->second.GetShares(); + const FlowStat::SharesMap *shares = map_it->GetShares(); uint32 prev_count = 0; for (FlowStat::SharesMap::const_iterator i = shares->begin(); i != shares->end(); ++i) { tmp.InsertOrRetrieve(i->second)->Update(i->first - prev_count); @@ -1596,9 +1596,9 @@ struct StationViewWindow : public Window { { const CargoDataEntry *source_dest = this->cached_destinations.Retrieve(i); for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) { - StationID from = it->first; + StationID from = it->GetOrigin(); const CargoDataEntry *source_entry = source_dest->Retrieve(from); - const FlowStat::SharesMap *shares = it->second.GetShares(); + const FlowStat::SharesMap *shares = it->GetShares(); for (FlowStat::SharesMap::const_iterator flow_it = shares->begin(); flow_it != shares->end(); ++flow_it) { const CargoDataEntry *via_entry = source_entry->Retrieve(flow_it->second); for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {