1
0
Fork 0

Codechange: Improve performance of evaluating group hierarchy.

Store a list of child groups in each group, to avoid having to iterate the group pool to find its children.
pull/14270/head
Peter Nelson 2025-05-21 17:14:28 +01:00 committed by Peter Nelson
parent 8f3f25de4b
commit 000a79c093
3 changed files with 63 additions and 23 deletions

View File

@ -11,6 +11,7 @@
#define GROUP_H
#include "group_type.h"
#include "core/flatset_type.hpp"
#include "core/pool_type.hpp"
#include "company_type.h"
#include "vehicle_type.h"
@ -78,6 +79,7 @@ struct Group : GroupPool::PoolItem<&_group_pool> {
Livery livery{}; ///< Custom colour scheme for vehicles in this group
GroupStatistics statistics{}; ///< NOSAVE: Statistics and caches on the vehicles in the group.
FlatSet<GroupID> children; ///< NOSAVE: child groups belonging to this group.
bool folded = false; ///< NOSAVE: Is this group folded in the group view?
GroupID parent = GroupID::Invalid(); ///< Parent group
@ -104,6 +106,7 @@ inline bool IsAllGroupID(GroupID id_g)
}
void UpdateGroupChildren();
uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e);
uint GetGroupNumVehicle(CompanyID company, GroupID id_g, VehicleType type);
uint GetGroupNumVehicleMinAge(CompanyID company, GroupID id_g, VehicleType type);

View File

@ -41,6 +41,16 @@ void GroupStatistics::Clear()
this->num_engines.clear();
}
/**
* Update children list for each group.
*/
void UpdateGroupChildren()
{
for (Group *g : Group::Iterate()) {
if (g->parent != GroupID::Invalid()) Group::Get(g->parent)->children.insert(g->index);
}
}
/**
* Get number of vehicles of a specific engine ID.
* @param engine Engine ID.
@ -294,12 +304,11 @@ static void PropagateChildLivery(const Group *g, bool reset_cache)
}
}
for (Group *cg : Group::Iterate()) {
if (cg->parent == g->index) {
if (!HasBit(cg->livery.in_use, 0)) cg->livery.colour1 = g->livery.colour1;
if (!HasBit(cg->livery.in_use, 1)) cg->livery.colour2 = g->livery.colour2;
PropagateChildLivery(cg, reset_cache);
}
for (const GroupID &childgroup : g->children) {
Group *cg = Group::Get(childgroup);
if (!HasBit(cg->livery.in_use, 0)) cg->livery.colour1 = g->livery.colour1;
if (!HasBit(cg->livery.in_use, 1)) cg->livery.colour2 = g->livery.colour2;
PropagateChildLivery(cg, reset_cache);
}
}
@ -333,7 +342,7 @@ std::tuple<CommandCost, GroupID> CmdCreateGroup(DoCommandFlags flags, VehicleTyp
if (!Group::CanAllocateItem()) return { CMD_ERROR, GroupID::Invalid() };
const Group *pg = Group::GetIfValid(parent_group);
Group *pg = Group::GetIfValid(parent_group);
if (pg != nullptr) {
if (pg->owner != _current_company) return { CMD_ERROR, GroupID::Invalid() };
if (pg->vehicle_type != vt) return { CMD_ERROR, GroupID::Invalid() };
@ -353,6 +362,7 @@ std::tuple<CommandCost, GroupID> CmdCreateGroup(DoCommandFlags flags, VehicleTyp
g->livery.colour1 = pg->livery.colour1;
g->livery.colour2 = pg->livery.colour2;
g->flags = pg->flags;
pg->children.insert(g->index);
}
InvalidateWindowData(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, _current_company).ToWindowNumber());
@ -380,10 +390,8 @@ CommandCost CmdDeleteGroup(DoCommandFlags flags, GroupID group_id)
Command<CMD_REMOVE_ALL_VEHICLES_GROUP>::Do(flags, group_id);
/* Delete sub-groups */
for (const Group *gp : Group::Iterate()) {
if (gp->parent == g->index) {
Command<CMD_DELETE_GROUP>::Do(flags, gp->index);
}
for (const GroupID &childgroup : g->children) {
Command<CMD_DELETE_GROUP>::Do(flags, childgroup);
}
if (flags.Test(DoCommandFlag::Execute)) {
@ -401,6 +409,11 @@ CommandCost CmdDeleteGroup(DoCommandFlags flags, GroupID group_id)
c->freegroups.ReleaseID(g->number);
}
if (g->parent != GroupID::Invalid()) {
Group *pg = Group::Get(g->parent);
pg->children.erase(g->index);
}
VehicleType vt = g->vehicle_type;
/* Delete the Replace Vehicle Windows */
@ -445,6 +458,9 @@ CommandCost CmdAlterGroup(DoCommandFlags flags, AlterGroupMode mode, GroupID gro
}
}
} else if (mode == AlterGroupMode::SetParent) {
/* Do nothing if the parent group isn't actually changed. */
if (g->parent == parent_id) return CommandCost();
/* Set group parent */
const Group *pg = Group::GetIfValid(parent_id);
@ -458,7 +474,10 @@ CommandCost CmdAlterGroup(DoCommandFlags flags, AlterGroupMode mode, GroupID gro
}
if (flags.Test(DoCommandFlag::Execute)) {
if (g->parent != GroupID::Invalid()) Group::Get(g->parent)->children.erase(g->index);
g->parent = (pg == nullptr) ? GroupID::Invalid() : pg->index;
if (g->parent != GroupID::Invalid()) Group::Get(g->parent)->children.insert(g->index);
GroupStatistics::UpdateAutoreplace(g->owner);
if (!HasBit(g->livery.in_use, 0) || !HasBit(g->livery.in_use, 1)) {
@ -696,8 +715,8 @@ static void SetGroupFlag(Group *g, GroupFlag flag, bool set, bool children)
if (!children) return;
for (Group *pg : Group::Iterate()) {
if (pg->parent == g->index) SetGroupFlag(pg, flag, set, true);
for (const GroupID &childgroup : g->children) {
SetGroupFlag(Group::Get(childgroup), flag, set, true);
}
}
@ -790,11 +809,14 @@ void UpdateTrainGroupID(Train *v)
uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e)
{
uint count = 0;
const Engine *e = Engine::Get(id_e);
for (const Group *g : Group::Iterate()) {
if (g->parent == id_g) count += GetGroupNumEngines(company, g->index, id_e);
if (Group *g = Group::GetIfValid(id_g); g != nullptr) {
for (const GroupID &childgroup : g->children) {
count += GetGroupNumEngines(company, childgroup, id_e);
}
}
return count + GroupStatistics::Get(company, id_g, e->type).GetNumEngines(id_e);
return count + GroupStatistics::Get(company, id_g, Engine::Get(id_e)->type).GetNumEngines(id_e);
}
/**
@ -808,9 +830,13 @@ uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e)
uint GetGroupNumVehicle(CompanyID company, GroupID id_g, VehicleType type)
{
uint count = 0;
for (const Group *g : Group::Iterate()) {
if (g->parent == id_g) count += GetGroupNumVehicle(company, g->index, type);
if (Group *g = Group::GetIfValid(id_g); g != nullptr) {
for (const GroupID &childgroup : g->children) {
count += GetGroupNumVehicle(company, childgroup, type);
}
}
return count + GroupStatistics::Get(company, id_g, type).num_vehicle;
}
@ -825,9 +851,13 @@ uint GetGroupNumVehicle(CompanyID company, GroupID id_g, VehicleType type)
uint GetGroupNumVehicleMinAge(CompanyID company, GroupID id_g, VehicleType type)
{
uint count = 0;
for (const Group *g : Group::Iterate()) {
if (g->parent == id_g) count += GetGroupNumVehicleMinAge(company, g->index, type);
if (Group *g = Group::GetIfValid(id_g); g != nullptr) {
for (const GroupID &childgroup : g->children) {
count += GetGroupNumVehicleMinAge(company, childgroup, type);
}
}
return count + GroupStatistics::Get(company, id_g, type).num_vehicle_min_age;
}
@ -842,9 +872,13 @@ uint GetGroupNumVehicleMinAge(CompanyID company, GroupID id_g, VehicleType type)
Money GetGroupProfitLastYearMinAge(CompanyID company, GroupID id_g, VehicleType type)
{
Money sum = 0;
for (const Group *g : Group::Iterate()) {
if (g->parent == id_g) sum += GetGroupProfitLastYearMinAge(company, g->index, type);
if (Group *g = Group::GetIfValid(id_g); g != nullptr) {
for (const GroupID &childgroup : g->children) {
sum += GetGroupProfitLastYearMinAge(company, childgroup, type);
}
}
return sum + GroupStatistics::Get(company, id_g, type).profit_last_year_min_age;
}

View File

@ -575,6 +575,9 @@ bool AfterLoadGame()
* that otherwise won't exist in the tree. */
RebuildViewportKdtree();
/* Group hierarchy may be evaluated during conversion, so ensure its correct early on. */
UpdateGroupChildren();
if (IsSavegameVersionBefore(SLV_98)) _gamelog.GRFAddList(_grfconfig);
if (IsSavegameVersionBefore(SLV_119)) {