From 7a7eed4fb7762cbc30bdec3a8039e77cd2e73866 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Fri, 17 Nov 2017 19:10:03 +0000 Subject: [PATCH] Add FirstEnabledTraitOrDefault helper method. This avoids the allocations caused by LINQ when using traits.FirstOrDefault(Exts.IsTraitEnabled). This is important in FrozenActorLayer.RefreshState which is called very often. We apply the new helper method to all areas using the old pattern. An overload that takes an array allows arrays to be enumerated without causing allocations. --- OpenRA.Game/Exts.cs | 20 +++++++++++++++++++ OpenRA.Game/Traits/Player/FrozenActorLayer.cs | 2 +- OpenRA.Mods.Common/Lint/CheckTooltips.cs | 2 +- .../Traits/Render/WithMakeAnimation.cs | 6 +++--- .../Traits/Render/WithWallSpriteBody.cs | 4 ++-- .../Traits/World/EditorActorPreview.cs | 4 ++-- .../Logic/Ingame/ProductionTooltipLogic.cs | 4 ++-- .../Widgets/ViewportControllerWidget.cs | 2 +- 8 files changed, 32 insertions(+), 12 deletions(-) diff --git a/OpenRA.Game/Exts.cs b/OpenRA.Game/Exts.cs index 1f3dc5261a..d51b9f0caa 100644 --- a/OpenRA.Game/Exts.cs +++ b/OpenRA.Game/Exts.cs @@ -503,6 +503,26 @@ namespace OpenRA { return IsTraitEnabled(t as object); } + + public static T FirstEnabledTraitOrDefault(this IEnumerable ts) + { + // PERF: Avoid LINQ. + foreach (var t in ts) + if (t.IsTraitEnabled()) + return t; + + return default(T); + } + + public static T FirstEnabledTraitOrDefault(this T[] ts) + { + // PERF: Avoid LINQ. + foreach (var t in ts) + if (t.IsTraitEnabled()) + return t; + + return default(T); + } } public static class Enum diff --git a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs index 51462a940a..0c36c29003 100644 --- a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs +++ b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs @@ -103,7 +103,7 @@ namespace OpenRA.Traits DamageState = health.DamageState; } - var tooltip = tooltips.FirstOrDefault(Exts.IsTraitEnabled); + var tooltip = tooltips.FirstEnabledTraitOrDefault(); if (tooltip != null) { TooltipInfo = tooltip.TooltipInfo; diff --git a/OpenRA.Mods.Common/Lint/CheckTooltips.cs b/OpenRA.Mods.Common/Lint/CheckTooltips.cs index c2576ce150..70d3cc4d17 100644 --- a/OpenRA.Mods.Common/Lint/CheckTooltips.cs +++ b/OpenRA.Mods.Common/Lint/CheckTooltips.cs @@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Lint if (buildable == null) continue; - var tooltip = actorInfo.Value.TraitInfos().FirstOrDefault(Exts.IsTraitEnabled); + var tooltip = actorInfo.Value.TraitInfos().FirstEnabledTraitOrDefault(); if (tooltip == null) emitError("The following buildable actor has no (enabled) Tooltip: " + actorInfo.Key); } diff --git a/OpenRA.Mods.Common/Traits/Render/WithMakeAnimation.cs b/OpenRA.Mods.Common/Traits/Render/WithMakeAnimation.cs index 21ea11c379..9ce59d9718 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithMakeAnimation.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithMakeAnimation.cs @@ -60,7 +60,7 @@ namespace OpenRA.Mods.Common.Traits.Render if (conditionManager != null && !string.IsNullOrEmpty(info.Condition) && token == ConditionManager.InvalidConditionToken) token = conditionManager.GrantCondition(self, info.Condition); - var wsb = wsbs.FirstOrDefault(Exts.IsTraitEnabled); + var wsb = wsbs.FirstEnabledTraitOrDefault(); if (wsb == null) return; @@ -80,7 +80,7 @@ namespace OpenRA.Mods.Common.Traits.Render if (conditionManager != null && !string.IsNullOrEmpty(info.Condition) && token == ConditionManager.InvalidConditionToken) token = conditionManager.GrantCondition(self, info.Condition); - var wsb = wsbs.FirstOrDefault(Exts.IsTraitEnabled); + var wsb = wsbs.FirstEnabledTraitOrDefault(); if (wsb == null) return; @@ -99,7 +99,7 @@ namespace OpenRA.Mods.Common.Traits.Render { Reverse(self, () => { - var wsb = wsbs.FirstOrDefault(Exts.IsTraitEnabled); + var wsb = wsbs.FirstEnabledTraitOrDefault(); // HACK: The actor remains alive and active for one tick before the followup activity // (sell/transform/etc) runs. This causes visual glitches that we attempt to minimize diff --git a/OpenRA.Mods.Common/Traits/Render/WithWallSpriteBody.cs b/OpenRA.Mods.Common/Traits/Render/WithWallSpriteBody.cs index 6410d18989..f4e347792f 100644 --- a/OpenRA.Mods.Common/Traits/Render/WithWallSpriteBody.cs +++ b/OpenRA.Mods.Common/Traits/Render/WithWallSpriteBody.cs @@ -47,7 +47,7 @@ namespace OpenRA.Mods.Common.Traits.Render var haveNeighbour = false; foreach (var n in kv.Value) { - var rb = init.World.Map.Rules.Actors[n].TraitInfos().FirstOrDefault(Exts.IsTraitEnabled); + var rb = init.World.Map.Rules.Actors[n].TraitInfos().FirstEnabledTraitOrDefault(); if (rb != null && rb.GetWallConnectionType() == Type) { haveNeighbour = true; @@ -119,7 +119,7 @@ namespace OpenRA.Mods.Common.Traits.Render foreach (var a in adjacentActors) { CVec facing; - var wc = a.TraitsImplementing().FirstOrDefault(Exts.IsTraitEnabled); + var wc = a.TraitsImplementing().FirstEnabledTraitOrDefault(); if (wc == null || !wc.AdjacentWallCanConnect(a, self.Location, wallInfo.Type, out facing)) continue; diff --git a/OpenRA.Mods.Common/Traits/World/EditorActorPreview.cs b/OpenRA.Mods.Common/Traits/World/EditorActorPreview.cs index acd876a3e2..8b0137a712 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorActorPreview.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorActorPreview.cs @@ -70,8 +70,8 @@ namespace OpenRA.Mods.Common.Traits Footprint = new ReadOnlyDictionary(footprint); } - var tooltip = Info.TraitInfos().FirstOrDefault(Exts.IsTraitEnabled) as TooltipInfoBase - ?? Info.TraitInfos().FirstOrDefault(Exts.IsTraitEnabled); + var tooltip = Info.TraitInfos().FirstEnabledTraitOrDefault() as TooltipInfoBase + ?? Info.TraitInfos().FirstEnabledTraitOrDefault(); Tooltip = (tooltip == null ? " < " + Info.Name + " >" : tooltip.Name) + "\n" + owner.Name + " (" + owner.Faction + ")" + "\nID: " + ID + "\nType: " + Info.Name; diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs index ab53bf5b3b..e55708847a 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs @@ -67,7 +67,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (actor == lastActor && hotkey == lastHotkey && pm.PowerState == lastPowerState) return; - var tooltip = actor.TraitInfos().FirstOrDefault(Exts.IsTraitEnabled); + var tooltip = actor.TraitInfos().FirstEnabledTraitOrDefault(); var name = tooltip != null ? tooltip.Name : actor.Name; var buildable = actor.TraitInfo(); var cost = actor.TraitInfo().Cost; @@ -135,7 +135,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic ActorInfo ai; if (rules.Actors.TryGetValue(a.ToLowerInvariant(), out ai)) { - var actorTooltip = ai.TraitInfos().FirstOrDefault(Exts.IsTraitEnabled); + var actorTooltip = ai.TraitInfos().FirstEnabledTraitOrDefault(); if (actorTooltip != null) return actorTooltip.Name; } diff --git a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs index c0f1218cfd..21ae51742c 100644 --- a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs @@ -235,7 +235,7 @@ namespace OpenRA.Mods.Common.Widgets if (underCursor != null) { - ActorTooltip = underCursor.TraitsImplementing().FirstOrDefault(Exts.IsTraitEnabled); + ActorTooltip = underCursor.TraitsImplementing().FirstEnabledTraitOrDefault(); if (ActorTooltip != null) { ActorTooltipExtra = underCursor.TraitsImplementing().ToArray();