Added comments in performance sensitive code.

This commit is contained in:
RoosterDragon
2015-12-04 19:38:20 +00:00
parent aaa82339d1
commit b0619a3e25
22 changed files with 76 additions and 11 deletions

View File

@@ -97,6 +97,9 @@ namespace OpenRA
}
}
// PERF: Cache all these traits as soon as the actor is created. This is a fairly cheap one-off cost per
// actor that allows us to provide some fast implementations of commonly used methods that are relied on by
// performance-sensitive parts of the core game engine, such as pathfinding, visibility and rendering.
Bounds = DetermineBounds();
VisualBounds = DetermineVisualBounds();
EffectiveOwner = TraitOrDefault<IEffectiveOwner>();
@@ -149,6 +152,7 @@ namespace OpenRA
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
// PERF: Avoid LINQ.
var renderables = Renderables(wr);
foreach (var modifier in renderModifiers)
renderables = modifier.ModifyRender(this, wr, renderables);
@@ -157,6 +161,13 @@ namespace OpenRA
IEnumerable<IRenderable> Renderables(WorldRenderer wr)
{
// PERF: Avoid LINQ.
// Implementations of Render are permitted to return both an eagerly materialized collection or a lazily
// generated sequence.
// For large amounts of renderables, a lazily generated sequence (e.g. as returned by LINQ, or by using
// `yield`) will avoid the need to allocate a large collection.
// For small amounts of renderables, allocating a small collection can often be faster and require less
// memory than creating the objects needed to represent a sequence.
foreach (var render in renders)
foreach (var renderable in render.Render(this, wr))
yield return renderable;
@@ -206,6 +217,7 @@ namespace OpenRA
public override string ToString()
{
// PERF: Avoid format strings.
var name = Info.Name + " " + ActorID;
if (!IsInWorld)
name += " (not in world)";
@@ -305,6 +317,7 @@ namespace OpenRA
public bool IsDisabled()
{
// PERF: Avoid LINQ.
foreach (var disable in disables)
if (disable.Disabled)
return true;
@@ -313,6 +326,7 @@ namespace OpenRA
public bool CanBeViewedByPlayer(Player player)
{
// PERF: Avoid LINQ.
foreach (var visibilityModifier in visibilityModifiers)
if (!visibilityModifier.IsVisible(this, player))
return false;

View File

@@ -113,6 +113,7 @@ namespace OpenRA
public static bool HasModifier(this Modifiers k, Modifiers mod)
{
// PERF: Enum.HasFlag is slower and requires allocations.
return (k & mod) == mod;
}

View File

@@ -40,6 +40,7 @@ namespace OpenRA.Graphics
glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph, Pair<char, Color>.EqualityComparer);
// PERF: Cache these delegates for Measure calls.
Func<char, float> characterWidth = character => glyphs[Pair.New(character, Color.White)].Advance;
lineWidth = line => line.Sum(characterWidth);

View File

@@ -22,6 +22,7 @@ namespace OpenRA.Graphics
{
public static bool Includes(this ScrollDirection d, ScrollDirection s)
{
// PERF: Enum.HasFlag is slower and requires allocations.
return (d & s) == s;
}

View File

@@ -1023,7 +1023,7 @@ namespace OpenRA
var uv = cell.ToMPos(this);
var terrainIndex = cachedTerrainIndexes[uv];
// Cache terrain indexes per cell on demand.
// PERF: Cache terrain indexes per cell on demand.
if (terrainIndex == InvalidCachedTerrainIndex)
{
var custom = CustomTerrain[uv];

View File

@@ -196,13 +196,20 @@ namespace OpenRA.Network
static Func<ISync, object> SerializableCopyOfMember(MemberExpression getMember, Type memberType, string name)
{
// We need to serialize a copy of the current value so if the sync report is generated, the values can
// be dumped as strings.
if (memberType.IsValueType)
{
// We can get a copy just by accessing the member since it is a value type.
// PERF: For value types we can avoid the overhead of calling ToString immediately. We can instead
// 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.
var boxedCopy = Expression.Convert(getMember, typeof(object));
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
// delay, as calling ToString later may produce different results.
return MemberToString(getMember, memberType, name);
}

View File

@@ -111,6 +111,7 @@ namespace OpenRA
public void SetViewportParams(int2 scroll, float zoom)
{
// PERF: Calling SetViewportParams on each renderer is slow. Only call it when things change.
var resolutionChanged = lastResolution != Resolution;
if (resolutionChanged)
{

View File

@@ -34,6 +34,9 @@ namespace OpenRA
}
}
/// <summary>
/// Provides efficient ways to query a set of actors by their traits.
/// </summary>
class TraitDictionary
{
static readonly Func<Type, ITraitContainer> CreateTraitContainer = t =>
@@ -161,6 +164,7 @@ namespace OpenRA
public IEnumerable<T> GetMultiple(uint actor)
{
// PERF: Custom enumerator for efficiency - using `yield` is slower.
++Queries;
return new MultipleEnumerable(this, actor);
}
@@ -197,6 +201,7 @@ namespace OpenRA
public IEnumerable<TraitPair<T>> All()
{
// PERF: Custom enumerator for efficiency - using `yield` is slower.
++Queries;
return new AllEnumerable(this);
}

View File

@@ -86,10 +86,9 @@ namespace OpenRA.Traits
{
var wasVisible = Visible;
Shrouded = true;
// We are doing the following LINQ manually for performance since this is a hot path.
// Visible = !Footprint.Any(shroud.IsVisible);
Visible = true;
// PERF: Avoid LINQ.
foreach (var puv in Footprint)
{
if (shroud.IsVisible(puv))
@@ -142,7 +141,7 @@ namespace OpenRA.Traits
public bool ShouldBeRemoved(Player owner)
{
// We use a loop here for performance reasons
// PERF: Avoid LINQ.
foreach (var rfa in removeFrozenActors)
if (rfa.RemoveActor(actor, owner))
return true;

View File

@@ -50,6 +50,7 @@ namespace OpenRA.Traits
{
public static bool HasStance(this Stance s, Stance stance)
{
// PERF: Enum.HasFlag is slower and requires allocations.
return (s & stance) == stance;
}
}
@@ -92,7 +93,11 @@ namespace OpenRA.Traits
public static class TargetModifiersExts
{
public static bool HasModifier(this TargetModifiers self, TargetModifiers m) { return (self & m) == m; }
public static bool HasModifier(this TargetModifiers self, TargetModifiers m)
{
// PERF: Enum.HasFlag is slower and requires allocations.
return (self & m) == m;
}
}
public interface IOrderTargeter

View File

@@ -74,10 +74,15 @@ namespace OpenRA.Traits
public static Activity RunActivity(Actor self, Activity act)
{
// PERF: If there are no activities we can bail straight away and save ourselves a call to
// Stopwatch.GetTimestamp.
if (act == null)
return act;
// Note - manual iteration here for performance due to high call volume.
// PERF: This is a hot path and must run with minimal added overhead.
// Calling Stopwatch.GetTimestamp is a bit expensive, so we enumerate manually to allow us to call it only
// once per iteration in the normal case.
// See also: DoTimed
var longTickThresholdInStopwatchTicks = PerfTimer.LongTickThresholdInStopwatchTicks;
var start = Stopwatch.GetTimestamp();
while (act != null)

View File

@@ -183,7 +183,7 @@ namespace OpenRA.Traits
for (var col = 0; col < cols; col++)
bins[row * cols + col] = new Bin();
// Cache this delegate so it does not have to be allocated repeatedly.
// PERF: Cache this delegate so it does not have to be allocated repeatedly.
actorShouldBeRemoved = removeActorPosition.Contains;
}
@@ -219,6 +219,7 @@ namespace OpenRA.Traits
public IEnumerable<Actor> GetActorsAt(CPos a)
{
// PERF: Custom enumerator for efficiency - using `yield` is slower.
var uv = a.ToMPos(map);
if (!influence.Contains(uv))
return Enumerable.Empty<Actor>();
@@ -534,6 +535,7 @@ namespace OpenRA.Traits
public IEnumerable<Actor> ActorsInBox(WPos a, WPos b)
{
// PERF: Inline BinsInBox here to avoid allocations as this method is called often.
var left = Math.Min(a.X, b.X);
var top = Math.Min(a.Y, b.Y);
var right = Math.Max(a.X, b.X);

View File

@@ -261,6 +261,7 @@ namespace OpenRA.Widgets
public virtual Rectangle GetEventBounds()
{
// PERF: Avoid LINQ.
var bounds = EventBounds;
foreach (var child in Children)
if (child.IsVisible())

View File

@@ -79,7 +79,12 @@ namespace OpenRA.Widgets
return new[] { (int)ss[0].Size.Y, (int)ss[1].Size.Y, (int)ss[2].Size.X, (int)ss[3].Size.X };
}
static bool HasFlags(this PanelSides a, PanelSides b) { return (a & b) == b; }
static bool HasFlags(this PanelSides a, PanelSides b)
{
// PERF: Enum.HasFlag is slower and requires allocations.
return (a & b) == b;
}
public static Rectangle InflateBy(this Rectangle rect, int l, int t, int r, int b)
{
return Rectangle.FromLTRB(rect.Left - l, rect.Top - t,

View File

@@ -43,7 +43,10 @@ namespace OpenRA
public static void DoTimed<T>(this IEnumerable<T> e, Action<T> a, string text)
{
// Note - manual enumeration here for performance due to high call volume.
// PERF: This is a hot path and must run with minimal added overhead.
// Calling Stopwatch.GetTimestamp is a bit expensive, so we enumerate manually to allow us to call it only
// once per iteration in the normal case.
// See also: RunActivity
var longTickThresholdInStopwatchTicks = PerfTimer.LongTickThresholdInStopwatchTicks;
using (var enumerator = e.GetEnumerator())
{