From 58e8b123db757f9c27a82d37eef1d00c81f67b81 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sun, 2 Jul 2023 16:35:51 +0100 Subject: [PATCH] Avoid some allocations during loading. - In FieldLoader, cache boxed bools and some boxed ints. - In FieldLoader, presize collections when parsing a List, HashSet or Dictionary. - In FieldLoader, don't allocate a list of missing items until required. - In FieldLoader, when a string value is passed, avoid wrapping this in a MiniYaml object by allowing both strings and yaml to be passed in the GetValue overload that does the real work. - In Animation, avoid allocating no-op actions. - In VxlReader, use EnsureCapcity to better size the Dictionary. - In VxlReader change VxlElement to a struct. - In Locomotor, presize TerrainSpeeds dictionary. --- OpenRA.Game/FieldLoader.cs | 38 +++++++++++++++----- OpenRA.Game/Graphics/Animation.cs | 8 ++--- OpenRA.Mods.Cnc/FileFormats/VxlReader.cs | 24 +++++++------ OpenRA.Mods.Cnc/Graphics/VoxelLoader.cs | 8 ++--- OpenRA.Mods.Common/Traits/World/Locomotor.cs | 6 ++-- 5 files changed, 55 insertions(+), 29 deletions(-) diff --git a/OpenRA.Game/FieldLoader.cs b/OpenRA.Game/FieldLoader.cs index f7e68aebbb..9864babac9 100644 --- a/OpenRA.Game/FieldLoader.cs +++ b/OpenRA.Game/FieldLoader.cs @@ -112,10 +112,19 @@ namespace OpenRA { typeof(Nullable<>), ParseNullable }, }; + static readonly object BoxedTrue = true; + static readonly object BoxedFalse = false; + static readonly object[] BoxedInts = Exts.MakeArray(33, i => (object)i); + static object ParseInt(string fieldName, Type fieldType, string value, MemberInfo field) { if (Exts.TryParseIntegerInvariant(value, out var res)) + { + if (res >= 0 && res < BoxedInts.Length) + return BoxedInts[res]; return res; + } + return InvalidValueAction(value, fieldType, fieldName); } @@ -361,7 +370,7 @@ namespace OpenRA static object ParseBool(string fieldName, Type fieldType, string value, MemberInfo field) { if (bool.TryParse(value.ToLowerInvariant(), out var result)) - return result; + return result ? BoxedTrue : BoxedFalse; return InvalidValueAction(value, fieldType, fieldName); } @@ -469,11 +478,11 @@ namespace OpenRA static object ParseHashSetOrList(string fieldName, Type fieldType, string value, MiniYaml yaml, MemberInfo field) { - var set = Activator.CreateInstance(fieldType); if (value == null) - return set; + return Activator.CreateInstance(fieldType); var parts = value.Split(SplitComma, StringSplitOptions.RemoveEmptyEntries); + var set = Activator.CreateInstance(fieldType, parts.Length); var arguments = fieldType.GetGenericArguments(); var addMethod = fieldType.GetMethod(nameof(List.Add), arguments); var addArgs = new object[1]; @@ -488,7 +497,10 @@ namespace OpenRA static object ParseDictionary(string fieldName, Type fieldType, string value, MiniYaml yaml, MemberInfo field) { - var dict = Activator.CreateInstance(fieldType); + if (yaml == null) + return Activator.CreateInstance(fieldType); + + var dict = Activator.CreateInstance(fieldType, yaml.Nodes.Count); var arguments = fieldType.GetGenericArguments(); var addMethod = fieldType.GetMethod(nameof(Dictionary.Add), arguments); var addArgs = new object[2]; @@ -527,7 +539,7 @@ namespace OpenRA public static void Load(object self, MiniYaml my) { var loadInfo = TypeLoadInfo[self.GetType()]; - var missing = new List(); + List missing = null; Dictionary md = null; @@ -542,6 +554,7 @@ namespace OpenRA val = fli.Loader(my); else { + missing ??= new List(); missing.Add(fli.YamlName); continue; } @@ -551,7 +564,11 @@ namespace OpenRA if (!TryGetValueFromYaml(fli.YamlName, fli.Field, md, out val)) { if (fli.Attribute.Required) + { + missing ??= new List(); missing.Add(fli.YamlName); + } + continue; } } @@ -559,7 +576,7 @@ namespace OpenRA fli.Field.SetValue(self, val); } - if (missing.Count > 0) + if (missing != null) throw new MissingFieldsException(missing.ToArray()); } @@ -620,12 +637,17 @@ namespace OpenRA public static object GetValue(string fieldName, Type fieldType, string value, MemberInfo field) { - return GetValue(fieldName, fieldType, new MiniYaml(value), field); + return GetValue(fieldName, fieldType, value, null, field); } public static object GetValue(string fieldName, Type fieldType, MiniYaml yaml, MemberInfo field) { - var value = yaml.Value?.Trim(); + return GetValue(fieldName, fieldType, yaml.Value, yaml, field); + } + + static object GetValue(string fieldName, Type fieldType, string value, MiniYaml yaml, MemberInfo field) + { + value = value?.Trim(); if (fieldType.IsGenericType) { if (GenericTypeParsers.TryGetValue(fieldType.GetGenericTypeDefinition(), out var parseFuncGeneric)) diff --git a/OpenRA.Game/Graphics/Animation.cs b/OpenRA.Game/Graphics/Animation.cs index 340582c0f9..c1565fad2f 100644 --- a/OpenRA.Game/Graphics/Animation.cs +++ b/OpenRA.Game/Graphics/Animation.cs @@ -30,7 +30,7 @@ namespace OpenRA.Graphics bool backwards; bool tickAlways; int timeUntilNextFrame; - Action tickFunc = () => { }; + Action tickFunc; public Animation(World world, string name) : this(world, name, () => WAngle.Zero) { } @@ -164,7 +164,7 @@ namespace OpenRA.Graphics if (frame >= CurrentSequence.Length) { frame = CurrentSequence.Length - 1; - tickFunc = () => { }; + tickFunc = null; after?.Invoke(); } }; @@ -212,13 +212,13 @@ namespace OpenRA.Graphics public void Tick(int t) { if (tickAlways) - tickFunc(); + tickFunc?.Invoke(); else { timeUntilNextFrame -= t; while (timeUntilNextFrame <= 0) { - tickFunc(); + tickFunc?.Invoke(); timeUntilNextFrame += CurrentSequenceTickOrDefault(); } } diff --git a/OpenRA.Mods.Cnc/FileFormats/VxlReader.cs b/OpenRA.Mods.Cnc/FileFormats/VxlReader.cs index c2e2fea2c6..7962452b80 100644 --- a/OpenRA.Mods.Cnc/FileFormats/VxlReader.cs +++ b/OpenRA.Mods.Cnc/FileFormats/VxlReader.cs @@ -15,10 +15,15 @@ using System.IO; namespace OpenRA.Mods.Cnc.FileFormats { public enum NormalType { TiberianSun = 2, RedAlert2 = 4 } - public class VxlElement + public readonly struct VxlElement { - public byte Color; - public byte Normal; + public readonly byte Color; + public readonly byte Normal; + public VxlElement(byte color, byte normal) + { + Color = color; + Normal = normal; + } } public class VxlLimb @@ -83,20 +88,17 @@ namespace OpenRA.Mods.Cnc.FileFormats var x = (byte)(i % l.Size[0]); var y = (byte)(i / l.Size[0]); byte z = 0; - l.VoxelMap[x, y] = new Dictionary(); + var voxelMap = new Dictionary(); + l.VoxelMap[x, y] = voxelMap; do { z += s.ReadUInt8(); var count = s.ReadUInt8(); + voxelMap.EnsureCapacity(voxelMap.Count + count); for (var j = 0; j < count; j++) { - var v = new VxlElement - { - Color = s.ReadUInt8(), - Normal = s.ReadUInt8() - }; - - l.VoxelMap[x, y].Add(z, v); + var v = new VxlElement(s.ReadUInt8(), s.ReadUInt8()); + voxelMap.Add(z, v); z++; } diff --git a/OpenRA.Mods.Cnc/Graphics/VoxelLoader.cs b/OpenRA.Mods.Cnc/Graphics/VoxelLoader.cs index f3baca2dd5..b47d97a7aa 100644 --- a/OpenRA.Mods.Cnc/Graphics/VoxelLoader.cs +++ b/OpenRA.Mods.Cnc/Graphics/VoxelLoader.cs @@ -57,7 +57,7 @@ namespace OpenRA.Mods.Cnc.Graphics sheetBuilder = CreateSheetBuilder(); } - Vertex[] GenerateSlicePlane(int su, int sv, Func first, Func second, Func coord) + Vertex[] GenerateSlicePlane(int su, int sv, Func first, Func second, Func coord) { var colors = new byte[su * sv]; var normals = new byte[su * sv]; @@ -68,8 +68,8 @@ namespace OpenRA.Mods.Cnc.Graphics for (var u = 0; u < su; u++) { var voxel = first(u, v) ?? second(u, v); - colors[c] = voxel == null ? (byte)0 : voxel.Color; - normals[c] = voxel == null ? (byte)0 : voxel.Normal; + colors[c] = voxel == null ? (byte)0 : voxel.Value.Color; + normals[c] = voxel == null ? (byte)0 : voxel.Value.Normal; c++; } } @@ -99,7 +99,7 @@ namespace OpenRA.Mods.Cnc.Graphics IEnumerable GenerateSlicePlanes(VxlLimb l) { - VxlElement Get(int x, int y, int z) + VxlElement? Get(int x, int y, int z) { if (x < 0 || y < 0 || z < 0) return null; diff --git a/OpenRA.Mods.Common/Traits/World/Locomotor.cs b/OpenRA.Mods.Common/Traits/World/Locomotor.cs index a415eb9290..a6018f4e4e 100644 --- a/OpenRA.Mods.Common/Traits/World/Locomotor.cs +++ b/OpenRA.Mods.Common/Traits/World/Locomotor.cs @@ -84,8 +84,9 @@ namespace OpenRA.Mods.Common.Traits protected static object LoadSpeeds(MiniYaml y) { - var ret = new Dictionary(); - foreach (var t in y.ToDictionary()["TerrainSpeeds"].Nodes) + var speeds = y.ToDictionary()["TerrainSpeeds"].Nodes; + var ret = new Dictionary(speeds.Count); + foreach (var t in speeds) { var speed = FieldLoader.GetValue("speed", t.Value.Value); if (speed > 0) @@ -98,6 +99,7 @@ namespace OpenRA.Mods.Common.Traits } } + ret.TrimExcess(); return ret; }