Skip to content

Commit bce0cc3

Browse files
authored
Performance optimizations (#3041)
1 parent af2cfad commit bce0cc3

7 files changed

Lines changed: 172 additions & 41 deletions

File tree

ImperatorToCK3/CK3/Cultures/PillarCollection.cs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,41 @@ public PillarCollection(ColorFactory colorFactory, OrderedDictionary<string, boo
1818
}
1919

2020
public Pillar? GetHeritageForId(string heritageId) {
21-
var heritages = this.AsValueEnumerable().Where(p => p.Type == "heritage").ToFrozenSet();
22-
if (mergedPillarsDict.TryGetValue(heritageId, out var mergedHeritageId)) {
23-
return heritages.AsValueEnumerable().FirstOrDefault(p => p.Id == mergedHeritageId);
21+
var idToLookup = mergedPillarsDict.TryGetValue(heritageId, out var mergedHeritageId)
22+
? mergedHeritageId
23+
: heritageId;
24+
if (heritagesById.TryGetValue(idToLookup, out var heritage)) {
25+
return heritage;
2426
}
25-
26-
return heritages.AsValueEnumerable().FirstOrDefault(p => p.Id == heritageId);
27+
28+
foreach (var pillar in this) {
29+
if (pillar.Type != "heritage" || pillar.Id != idToLookup) {
30+
continue;
31+
}
32+
heritagesById[idToLookup] = pillar;
33+
return pillar;
34+
}
35+
36+
return null;
2737
}
2838

2939
public Pillar? GetLanguageForId(string languageId) {
30-
var languages = this.AsValueEnumerable().Where(p => p.Type == "language").ToFrozenSet();
31-
if (mergedPillarsDict.TryGetValue(languageId, out var mergedLanguageId)) {
32-
return languages.AsValueEnumerable().FirstOrDefault(p => p.Id == mergedLanguageId);
40+
var idToLookup = mergedPillarsDict.TryGetValue(languageId, out var mergedLanguageId)
41+
? mergedLanguageId
42+
: languageId;
43+
if (languagesById.TryGetValue(idToLookup, out var language)) {
44+
return language;
3345
}
34-
35-
return languages.AsValueEnumerable().FirstOrDefault(p => p.Id == languageId);
46+
47+
foreach (var pillar in this) {
48+
if (pillar.Type != "language" || pillar.Id != idToLookup) {
49+
continue;
50+
}
51+
languagesById[idToLookup] = pillar;
52+
return pillar;
53+
}
54+
55+
return null;
3656
}
3757

3858
public void LoadPillars(ModFilesystem ck3ModFS, OrderedDictionary<string, bool> ck3ModFlags) {
@@ -73,6 +93,13 @@ private void LoadPillar(string pillarId, BufferedReader pillarReader, OrderedDic
7393
}
7494
var pillar = new Pillar(pillarId, pillarData);
7595
AddOrReplace(pillar);
96+
if (pillar.Type == "heritage") {
97+
heritagesById[pillarId] = pillar;
98+
languagesById.Remove(pillarId);
99+
} else if (pillar.Type == "language") {
100+
languagesById[pillarId] = pillar;
101+
heritagesById.Remove(pillarId);
102+
}
76103

77104
// Perform some non-breaking validation.
78105
if (pillar.Type == "heritage") {
@@ -148,6 +175,8 @@ private void LoadInvalidatingPillarIds(OrderedDictionary<string, bool> ck3ModFla
148175

149176
private PillarData pillarData = new();
150177
private readonly Parser pillarDataParser = new();
178+
private readonly Dictionary<string, Pillar> heritagesById = new(StringComparer.Ordinal);
179+
private readonly Dictionary<string, Pillar> languagesById = new(StringComparer.Ordinal);
151180

152181
private readonly IgnoredKeywordsSet ignoredModFlags = [];
153182
}

ImperatorToCK3/CK3/Dynasties/HouseCollection.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using commonItems.Mods;
44
using ImperatorToCK3.CK3.Characters;
55
using System;
6-
using System.Collections.Frozen;
76
using System.Collections.Generic;
87
using ZLinq;
98

@@ -33,12 +32,12 @@ public void LoadCK3Houses(ModFilesystem ck3ModFS) {
3332
internal void PurgeUnneededHouses(CharacterCollection ck3Characters, Date date) {
3433
Logger.Info("Purging unneeded dynasty houses...");
3534

36-
FrozenSet<string> houseIdsToKeep = ck3Characters.AsValueEnumerable()
37-
.Select(c => c.GetDynastyHouseId(date))
38-
.Where(id => id is not null)
39-
.Distinct()
40-
.Cast<string>()
41-
.ToFrozenSet();
35+
HashSet<string> houseIdsToKeep = new(StringComparer.Ordinal);
36+
foreach (var character in ck3Characters) {
37+
if (character.GetDynastyHouseId(date) is string houseId) {
38+
houseIdsToKeep.Add(houseId);
39+
}
40+
}
4241

4342
int removedCount = 0;
4443
foreach (var house in this.AsValueEnumerable().ToArray()) {

ImperatorToCK3/CK3/Titles/LandedTitles.cs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ internal sealed class LandedTitles : TitleCollection {
4646
.Where(t => t.CapitalBaronyProvinceId.HasValue)
4747
.Select(t => t.CapitalBaronyProvinceId!.Value)
4848
.ToFrozenSet();
49+
private Dictionary<ulong, Title>? countyByProvinceId;
4950

5051
public void LoadTitles(ModFilesystem ck3ModFS, CK3LocDB ck3LocDB, ColorFactory colorFactory) {
5152
Logger.Info("Loading landed titles...");
@@ -356,14 +357,38 @@ private static void RemoveTitleFromDeJureRelationships(Title titleToErase) {
356357
}
357358

358359
public Title? GetCountyForProvince(ulong provinceId) {
359-
foreach (var county in this.Where(title => title.Rank == TitleRank.county)) {
360-
if (county.CountyProvinceIds.Contains(provinceId)) {
361-
return county;
360+
if (countyByProvinceId is null) {
361+
countyByProvinceId = BuildCountyByProvinceLookup();
362+
}
363+
364+
if (countyByProvinceId.TryGetValue(provinceId, out var county) && county.CountyProvinceIds.Contains(provinceId)) {
365+
return county;
366+
}
367+
368+
foreach (var title in this) {
369+
if (title.Rank != TitleRank.county || !title.CountyProvinceIds.Contains(provinceId)) {
370+
continue;
362371
}
372+
countyByProvinceId[provinceId] = title;
373+
return title;
363374
}
375+
364376
return null;
365377
}
366378

379+
private Dictionary<ulong, Title> BuildCountyByProvinceLookup() {
380+
var lookup = new Dictionary<ulong, Title>();
381+
foreach (var title in this) {
382+
if (title.Rank != TitleRank.county) {
383+
continue;
384+
}
385+
foreach (var provinceId in title.CountyProvinceIds) {
386+
lookup[provinceId] = title;
387+
}
388+
}
389+
return lookup;
390+
}
391+
367392
public Title? GetBaronyForProvince(ulong provinceId) {
368393
var baronies = this.Where(title => title.Rank == TitleRank.barony);
369394
return baronies.FirstOrDefault(b => provinceId == b?.ProvinceId, defaultValue: null);

ImperatorToCK3/Imperator/Geography/Area.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using ImperatorToCK3.Imperator.Provinces;
55
using System.Collections.Generic;
66
using System.Collections.Immutable;
7-
using System.Linq;
87

98
namespace ImperatorToCK3.Imperator.Geography;
109

@@ -29,8 +28,39 @@ public bool ContainsProvince(ulong provinceId) {
2928
return ProvinceIds.Contains(provinceId);
3029
}
3130

32-
public IReadOnlySet<Province> Provinces => provinceCollection.Where(p => ContainsProvince(p.Id)).ToImmutableSortedSet();
31+
public IReadOnlySet<Province> Provinces {
32+
get {
33+
EnsureProvinceCachesInitialized();
34+
return cachedProvinces!;
35+
}
36+
}
37+
38+
public bool TryGetProvince(ulong provinceId, out Province province) {
39+
EnsureProvinceCachesInitialized();
40+
return provincesById!.TryGetValue(provinceId, out province!);
41+
}
42+
43+
private void EnsureProvinceCachesInitialized() {
44+
if (cachedProvinces is not null) {
45+
return;
46+
}
47+
48+
var cachedProvincesBuilder = ImmutableSortedSet.CreateBuilder<Province>();
49+
var provincesByIdCache = new Dictionary<ulong, Province>();
50+
foreach (var provinceId in ProvinceIds) {
51+
if (!provinceCollection.TryGetValue(provinceId, out var province)) {
52+
continue;
53+
}
54+
cachedProvincesBuilder.Add(province);
55+
provincesByIdCache[provinceId] = province;
56+
}
57+
58+
cachedProvinces = cachedProvincesBuilder.ToImmutable();
59+
provincesById = provincesByIdCache;
60+
}
3361

3462
private readonly ProvinceCollection provinceCollection;
63+
private ImmutableSortedSet<Province>? cachedProvinces;
64+
private Dictionary<ulong, Province>? provincesById;
3565
public static readonly IgnoredKeywordsSet IgnoredKeywords = new();
3666
}

ImperatorToCK3/Imperator/Jobs/Governorship.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System;
77
using System.Collections.Generic;
88
using System.Collections.Immutable;
9-
using System.Linq;
109

1110
namespace ImperatorToCK3.Imperator.Jobs;
1211

@@ -46,15 +45,27 @@ public Governorship(BufferedReader governorshipReader, CountryCollection countri
4645
}
4746

4847
public ImmutableArray<Province> GetIRProvinces(ProvinceCollection irProvinces) {
49-
return irProvinces
50-
.Where(p => p.OwnerCountry == Country && Region.ContainsProvince(p.Id))
51-
.ToImmutableArray();
48+
var provinces = ImmutableArray.CreateBuilder<Province>();
49+
foreach (var province in irProvinces) {
50+
if (province.OwnerCountry != Country || !Region.ContainsProvince(province.Id)) {
51+
continue;
52+
}
53+
provinces.Add(province);
54+
}
55+
return provinces.ToImmutable();
5256
}
5357

5458
public ImmutableArray<ulong> GetCK3ProvinceIds(ProvinceCollection irProvinces, ProvinceMapper provMapper) {
55-
return GetIRProvinces(irProvinces)
56-
.Select(p => p.Id)
57-
.SelectMany(provMapper.GetCK3ProvinceNumbers)
58-
.ToImmutableArray();
59+
var ck3ProvinceIds = ImmutableArray.CreateBuilder<ulong>();
60+
foreach (var irProvince in irProvinces) {
61+
if (irProvince.OwnerCountry != Country || !Region.ContainsProvince(irProvince.Id)) {
62+
continue;
63+
}
64+
65+
foreach (var ck3ProvinceId in provMapper.GetCK3ProvinceNumbers(irProvince.Id)) {
66+
ck3ProvinceIds.Add(ck3ProvinceId);
67+
}
68+
}
69+
return ck3ProvinceIds.ToImmutable();
5970
}
6071
}

ImperatorToCK3/Imperator/States/State.cs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using ImperatorToCK3.Imperator.Geography;
44
using ImperatorToCK3.Imperator.Provinces;
55
using System.Collections.Generic;
6-
using System.Linq;
6+
using System.Collections.Immutable;
77

88
namespace ImperatorToCK3.Imperator.States;
99

@@ -21,6 +21,37 @@ public State(ulong id, StateData stateData) {
2121
Country = stateData.Country!;
2222
}
2323

24-
public Province CapitalProvince => Area.Provinces.First(p => p.Id == capitalProvinceId);
25-
public IEnumerable<Province> Provinces => Area.Provinces.Where(p => p.State?.Id == Id);
24+
public Province CapitalProvince {
25+
get {
26+
if (capitalProvince is not null) {
27+
return capitalProvince;
28+
}
29+
if (Area.TryGetProvince(capitalProvinceId, out var areaProvince)) {
30+
capitalProvince = areaProvince;
31+
return areaProvince;
32+
}
33+
throw new KeyNotFoundException($"Capital province {capitalProvinceId} was not found in area {Area.Id} for state {Id}.");
34+
}
35+
}
36+
37+
public IEnumerable<Province> Provinces {
38+
get {
39+
if (provinces is not null) {
40+
return provinces;
41+
}
42+
43+
var stateProvinces = ImmutableArray.CreateBuilder<Province>();
44+
foreach (var province in Area.Provinces) {
45+
if (province.State?.Id == Id) {
46+
stateProvinces.Add(province);
47+
}
48+
}
49+
50+
provinces = stateProvinces.ToImmutable();
51+
return provinces;
52+
}
53+
}
54+
55+
private Province? capitalProvince;
56+
private ImmutableArray<Province>? provinces;
2657
}

ImperatorToCK3/Mappers/Region/CK3Region.cs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
using commonItems;
22
using commonItems.Serialization;
33
using ImperatorToCK3.CK3.Titles;
4-
using System;
54
using System.Collections.Generic;
65
using System.Text;
7-
using ZLinq;
86

97
namespace ImperatorToCK3.Mappers.Region;
108

@@ -77,14 +75,22 @@ public void LinkCounty(Title theCounty) {
7775
Counties[theCounty.Id] = theCounty;
7876
}
7977
public bool ContainsProvince(ulong provinceId) {
80-
if (Regions.Values.AsValueEnumerable().Any(region => region.ContainsProvince(provinceId))) {
81-
return true;
78+
foreach (var region in Regions.Values) {
79+
if (region.ContainsProvince(provinceId)) {
80+
return true;
81+
}
8282
}
83-
if (Duchies.Values.AsValueEnumerable().Any(duchy => duchy.DuchyContainsProvince(provinceId))) {
84-
return true;
83+
foreach (var duchy in Duchies.Values) {
84+
if (duchy.DuchyContainsProvince(provinceId)) {
85+
return true;
86+
}
8587
}
86-
if (Counties.Values.AsValueEnumerable().Any(county => county.CountyProvinceIds.AsValueEnumerable().Contains(provinceId))) {
87-
return true;
88+
foreach (var county in Counties.Values) {
89+
foreach (var countyProvinceId in county.CountyProvinceIds) {
90+
if (countyProvinceId == provinceId) {
91+
return true;
92+
}
93+
}
8894
}
8995
return Provinces.Contains(provinceId);
9096
}

0 commit comments

Comments
 (0)