From 30b1f926f2eea96020b401cf54b0c8d30cc8d231 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Fri, 7 Jul 2023 19:32:01 +0100 Subject: [PATCH] Improve performance of MiniYaml inheritance tree tracking. Use an ImmutableDictionary to avoid having to clone the inheritance tree in ResolveInherits. This avoids a lot of dictionary clones. --- OpenRA.Game/MiniYaml.cs | 28 ++++++++++++++-------------- OpenRA.Game/OpenRA.Game.csproj | 3 +++ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/OpenRA.Game/MiniYaml.cs b/OpenRA.Game/MiniYaml.cs index 2a9892a4b6..851742da11 100644 --- a/OpenRA.Game/MiniYaml.cs +++ b/OpenRA.Game/MiniYaml.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using OpenRA.FileSystem; @@ -324,22 +325,19 @@ namespace OpenRA var resolved = new Dictionary(tree.Count); foreach (var kv in tree) { - var inherited = new Dictionary - { - { kv.Key, default } - }; - + // Inheritance is tracked from parent->child, but not from child->parentsiblings. + var inherited = ImmutableDictionary.Empty.Add(kv.Key, default); var children = ResolveInherits(kv.Value, tree, inherited); resolved.Add(kv.Key, new MiniYaml(kv.Value.Value, children)); } // Resolve any top-level removals (e.g. removing whole actor blocks) var nodes = new MiniYaml("", resolved.Select(kv => new MiniYamlNode(kv.Key, kv.Value)).ToList()); - return ResolveInherits(nodes, tree, new Dictionary()); + return ResolveInherits(nodes, tree, ImmutableDictionary.Empty); } static void MergeIntoResolved(MiniYamlNode overrideNode, List existingNodes, HashSet existingNodeKeys, - Dictionary tree, Dictionary inherited) + Dictionary tree, ImmutableDictionary inherited) { if (existingNodeKeys.Add(overrideNode.Key)) { @@ -352,14 +350,11 @@ namespace OpenRA existingNode.Value.Nodes = ResolveInherits(existingNode.Value, tree, inherited); } - static List ResolveInherits(MiniYaml node, Dictionary tree, Dictionary inherited) + static List ResolveInherits(MiniYaml node, Dictionary tree, ImmutableDictionary inherited) { var resolved = new List(node.Nodes.Count); var resolvedKeys = new HashSet(node.Nodes.Count); - // Inheritance is tracked from parent->child, but not from child->parentsiblings. - inherited = new Dictionary(inherited); - foreach (var n in node.Nodes) { if (n.Key == "Inherits" || n.Key.StartsWith("Inherits@", StringComparison.Ordinal)) @@ -368,10 +363,15 @@ namespace OpenRA throw new YamlException( $"{n.Location}: Parent type `{n.Value.Value}` not found"); - if (inherited.TryGetValue(n.Value.Value, out var location)) - throw new YamlException($"{n.Location}: Parent type `{n.Value.Value}` was already inherited by this yaml tree at {location} (note: may be from a derived tree)"); + try + { + inherited = inherited.Add(n.Value.Value, n.Location); + } + catch (ArgumentException) + { + throw new YamlException($"{n.Location}: Parent type `{n.Value.Value}` was already inherited by this yaml tree at {inherited[n.Value.Value]} (note: may be from a derived tree)"); + } - inherited.Add(n.Value.Value, n.Location); foreach (var r in ResolveInherits(parent, tree, inherited)) MergeIntoResolved(r, resolved, resolvedKeys, tree, inherited); } diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 085a2fed4f..2e83f52ae5 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -6,6 +6,9 @@ + + +