Merge pull request #12599 from RoosterDragon/allocs

Reduce ongoing allocations
This commit is contained in:
reaperrr
2017-01-27 17:52:30 +01:00
committed by GitHub
5 changed files with 86 additions and 30 deletions

View File

@@ -166,8 +166,10 @@ namespace OpenRA.Network
struct TypeInfo
{
static ParameterExpression syncParam = Expression.Parameter(typeof(ISync), "sync");
static ConstantExpression nullString = Expression.Constant(null, typeof(string));
static readonly ParameterExpression SyncParam = Expression.Parameter(typeof(ISync), "sync");
static readonly ConstantExpression NullString = Expression.Constant(null, typeof(string));
static readonly ConstantExpression TrueString = Expression.Constant(bool.TrueString, typeof(string));
static readonly ConstantExpression FalseString = Expression.Constant(bool.FalseString, typeof(string));
public readonly Func<ISync, object>[] SerializableCopyOfMemberFunctions;
public readonly string[] Names;
@@ -184,7 +186,7 @@ namespace OpenRA.Network
"Properties using the Sync attribute must be readable and must not use index parameters.\n" +
"Invalid Property: " + prop.DeclaringType.FullName + "." + prop.Name);
var sync = Expression.Convert(syncParam, type);
var sync = Expression.Convert(SyncParam, type);
SerializableCopyOfMemberFunctions = fields
.Select(fi => SerializableCopyOfMember(Expression.Field(sync, fi), fi.FieldType, fi.Name))
.Concat(properties.Select(pi => SerializableCopyOfMember(Expression.Property(sync, pi), pi.PropertyType, pi.Name)))
@@ -203,8 +205,16 @@ namespace OpenRA.Network
// just box a copy of the current value into an object. This is faster than calling ToString. We
// can call ToString later when we generate the report. Most of the time, the sync report is never
// generated so we successfully avoid the overhead to calling ToString.
if (memberType == typeof(bool))
{
// PERF: If the member is a Boolean, we can also avoid the allocation caused by boxing it.
// Instead, we can just return the resulting strings directly.
var getBoolString = Expression.Condition(getMember, TrueString, FalseString);
return Expression.Lambda<Func<ISync, string>>(getBoolString, name, new[] { SyncParam }).Compile();
}
var boxedCopy = Expression.Convert(getMember, typeof(object));
return Expression.Lambda<Func<ISync, object>>(boxedCopy, name, new[] { syncParam }).Compile();
return Expression.Lambda<Func<ISync, object>>(boxedCopy, name, new[] { SyncParam }).Compile();
}
// For reference types, we have to call ToString right away to get a snapshot of the value. We cannot
@@ -231,10 +241,10 @@ namespace OpenRA.Network
var member = Expression.Block(new[] { memberVariable }, assignMemberVariable);
getString = Expression.Call(member, toString);
var nullMember = Expression.Constant(null, memberType);
getString = Expression.Condition(Expression.Equal(member, nullMember), nullString, getString);
getString = Expression.Condition(Expression.Equal(member, nullMember), NullString, getString);
}
return Expression.Lambda<Func<ISync, string>>(getString, name, new[] { syncParam }).Compile();
return Expression.Lambda<Func<ISync, string>>(getString, name, new[] { SyncParam }).Compile();
}
}
}

View File

@@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Traits
public class AutoTarget : ConditionalTrait<AutoTargetInfo>, INotifyIdle, INotifyDamage, ITick, IResolveOrder, ISync
{
readonly AttackBase[] attackBases;
readonly IEnumerable<AttackBase> activeAttackBases;
readonly AttackFollow[] attackFollows;
[Sync] int nextScanTime = 0;
@@ -67,7 +67,7 @@ namespace OpenRA.Mods.Common.Traits
: base(info)
{
var self = init.Self;
attackBases = self.TraitsImplementing<AttackBase>().ToArray();
activeAttackBases = self.TraitsImplementing<AttackBase>().ToArray().Where(Exts.IsTraitEnabled);
if (init.Contains<StanceInit>())
Stance = init.Get<StanceInit, UnitStance>();
@@ -105,7 +105,8 @@ namespace OpenRA.Mods.Common.Traits
}
// not a lot we can do about things we can't hurt... although maybe we should automatically run away?
if (attackBases.All(a => a.IsTraitDisabled || !a.HasAnyValidWeapons(Target.FromActor(attacker))))
var attackerAsTarget = Target.FromActor(attacker);
if (!activeAttackBases.Any(a => a.HasAnyValidWeapons(attackerAsTarget)))
return;
// don't retaliate against own units force-firing on us. It's usually not what the player wanted.
@@ -117,8 +118,9 @@ namespace OpenRA.Mods.Common.Traits
return;
Aggressor = attacker;
var allowMove = Info.AllowMovement && Stance != UnitStance.Defend;
if (attackFollows.All(a => a.IsTraitDisabled || !a.IsReachableTarget(a.Target, allowMove)))
bool allowMove;
if (ShouldAttack(out allowMove))
Attack(self, Aggressor, allowMove);
}
@@ -130,11 +132,23 @@ namespace OpenRA.Mods.Common.Traits
if (Stance < UnitStance.Defend || !Info.TargetWhenIdle)
return;
var allowMove = Info.AllowMovement && Stance != UnitStance.Defend;
if (attackFollows.All(a => a.IsTraitDisabled || !a.IsReachableTarget(a.Target, allowMove)))
bool allowMove;
if (ShouldAttack(out allowMove))
ScanAndAttack(self, allowMove);
}
bool ShouldAttack(out bool allowMove)
{
allowMove = Info.AllowMovement && Stance != UnitStance.Defend;
// PERF: Avoid LINQ.
foreach (var attackFollow in attackFollows)
if (!attackFollow.IsTraitDisabled && attackFollow.IsReachableTarget(attackFollow.Target, allowMove))
return false;
return true;
}
public void Tick(Actor self)
{
if (IsTraitDisabled)
@@ -146,8 +160,7 @@ namespace OpenRA.Mods.Common.Traits
public Actor ScanForTarget(Actor self, bool allowMove)
{
var activeAttackBases = attackBases.Where(Exts.IsTraitEnabled);
if (activeAttackBases.Any() && nextScanTime <= 0)
if (nextScanTime <= 0 && activeAttackBases.Any())
{
nextScanTime = self.World.SharedRandom.Next(Info.MinimumScanTimeInterval, Info.MaximumScanTimeInterval);
@@ -160,8 +173,6 @@ namespace OpenRA.Mods.Common.Traits
var range = Info.ScanRadius > 0 ? WDist.FromCells(Info.ScanRadius) : ab.GetMaximumRange();
return ChooseTarget(self, ab, attackStances, range, allowMove);
}
continue;
}
}
@@ -181,7 +192,6 @@ namespace OpenRA.Mods.Common.Traits
var target = Target.FromActor(targetActor);
self.SetTargetLine(target, Color.Red, false);
var activeAttackBases = attackBases.Where(Exts.IsTraitEnabled);
foreach (var ab in activeAttackBases)
ab.AttackTarget(target, false, allowMove);
}

View File

@@ -92,8 +92,8 @@ namespace OpenRA.Mods.Common.Traits
foreach (var kvp in palettes)
{
if ((info.Palettes.Count > 0 && !info.Palettes.Any(kvp.Key.StartsWith))
|| (info.ExcludePalettes.Count > 0 && info.ExcludePalettes.Any(kvp.Key.StartsWith)))
if ((info.Palettes.Count > 0 && !AnyPaletteNameStartsWith(info.Palettes, kvp.Key))
|| (info.ExcludePalettes.Count > 0 && AnyPaletteNameStartsWith(info.ExcludePalettes, kvp.Key)))
continue;
var palette = kvp.Value;
@@ -105,5 +105,15 @@ namespace OpenRA.Mods.Common.Traits
palette[info.RotationBase + i] = rotationBuffer[i];
}
}
static bool AnyPaletteNameStartsWith(HashSet<string> names, string prefix)
{
// PERF: Avoid LINQ.
foreach (var name in names)
if (name.StartsWith(prefix))
return true;
return false;
}
}
}

View File

@@ -9,6 +9,7 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
@@ -29,6 +30,9 @@ namespace OpenRA.Mods.Common.Traits.Radar
readonly AppearsOnRadarInfo info;
IRadarColorModifier modifier;
Color currentColor = Color.Transparent;
Func<Pair<CPos, SubCell>, Pair<CPos, Color>> cellToSignature;
public AppearsOnRadar(AppearsOnRadarInfo info)
{
this.info = info;
@@ -48,7 +52,14 @@ namespace OpenRA.Mods.Common.Traits.Radar
if (info.UseLocation)
return new[] { Pair.New(self.Location, color) };
return self.OccupiesSpace.OccupiedCells().Select(c => Pair.New(c.First, color));
// PERF: Cache cellToSignature delegate to avoid allocations as color does not change often.
if (currentColor != color)
{
currentColor = color;
cellToSignature = c => Pair.New(c.First, color);
}
return self.OccupiesSpace.OccupiedCells().Select(cellToSignature);
}
}
}

View File

@@ -41,7 +41,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public int[] SelectionBoxBounds { get { return VisualBounds; } }
}
public class SelectionDecorations : IRenderAboveShroud, ITick
public class SelectionDecorations : IRenderAboveShroud, INotifyCreated, ITick
{
// depends on the order of pips in TraitsInterfaces.cs!
static readonly string[] PipStrings = { "pip-empty", "pip-green", "pip-yellow", "pip-red", "pip-gray", "pip-blue", "pip-ammo", "pip-ammoempty" };
@@ -49,6 +49,7 @@ namespace OpenRA.Mods.Common.Traits.Render
public readonly SelectionDecorationsInfo Info;
readonly Animation pipImages;
IPips[] pipSources;
public SelectionDecorations(Actor self, SelectionDecorationsInfo info)
{
@@ -57,6 +58,11 @@ namespace OpenRA.Mods.Common.Traits.Render
pipImages = new Animation(self.World, Info.Image);
}
void INotifyCreated.Created(Actor self)
{
pipSources = self.TraitsImplementing<IPips>().ToArray();
}
IEnumerable<WPos> ActivityTargetPath(Actor self)
{
if (!self.IsInWorld || self.IsDead)
@@ -76,8 +82,13 @@ namespace OpenRA.Mods.Common.Traits.Render
IEnumerable<IRenderable> IRenderAboveShroud.RenderAboveShroud(Actor self, WorldRenderer wr)
{
if (self.World.FogObscures(self))
yield break;
return Enumerable.Empty<IRenderable>();
return DrawDecorations(self, wr);
}
IEnumerable<IRenderable> DrawDecorations(Actor self, WorldRenderer wr)
{
var selected = self.World.Selection.Contains(self);
var regularWorld = self.World.Type == WorldType.Regular;
var statusBars = Game.Settings.Game.StatusBars;
@@ -108,21 +119,25 @@ namespace OpenRA.Mods.Common.Traits.Render
if (self.World.LocalPlayer != null && self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug)
yield return new TargetLineRenderable(ActivityTargetPath(self), Color.Green);
foreach (var r in DrawPips(self, wr))
yield return r;
}
IEnumerable<IRenderable> DrawPips(Actor self, WorldRenderer wr)
{
if (pipSources.Length == 0)
return Enumerable.Empty<IRenderable>();
var b = self.VisualBounds;
var pos = wr.ScreenPxPosition(self.CenterPosition);
var bl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Bottom));
var pal = wr.Palette(Info.Palette);
foreach (var r in DrawPips(self, wr, bl, pal))
yield return r;
return DrawPips(self, bl, pal);
}
IEnumerable<IRenderable> DrawPips(Actor self, WorldRenderer wr, int2 basePosition, PaletteReference palette)
IEnumerable<IRenderable> DrawPips(Actor self, int2 basePosition, PaletteReference palette)
{
var pipSources = self.TraitsImplementing<IPips>();
if (!pipSources.Any())
yield break;
pipImages.PlayRepeating(PipStrings[0]);
var pipSize = pipImages.Image.Size.XY.ToInt2();