From eb3be990a663c1b9e973105e373e3000a3c9bdaf Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sat, 30 Jan 2016 00:25:57 +0000 Subject: [PATCH 1/2] Remove dead code and encapsulate more in Sync.cs. --- OpenRA.Game/Sync.cs | 27 ++++++------------- .../Traits/Player/MissionObjectives.cs | 2 +- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/OpenRA.Game/Sync.cs b/OpenRA.Game/Sync.cs index 9492016424..85afe84109 100644 --- a/OpenRA.Game/Sync.cs +++ b/OpenRA.Game/Sync.cs @@ -38,12 +38,11 @@ namespace OpenRA { typeof(int2), ((Func)HashInt2).Method }, { typeof(CPos), ((Func)HashCPos).Method }, { typeof(CVec), ((Func)HashCVec).Method }, - { typeof(WDist), ((Func)Hash).Method }, - { typeof(WPos), ((Func)Hash).Method }, - { typeof(WVec), ((Func)Hash).Method }, - { typeof(WAngle), ((Func)Hash).Method }, - { typeof(WRot), ((Func)Hash).Method }, - { typeof(TypeDictionary), ((Func)HashTDict).Method }, + { typeof(WDist), ((Func)HashUsingHashCode).Method }, + { typeof(WPos), ((Func)HashUsingHashCode).Method }, + { typeof(WVec), ((Func)HashUsingHashCode).Method }, + { typeof(WAngle), ((Func)HashUsingHashCode).Method }, + { typeof(WRot), ((Func)HashUsingHashCode).Method }, { typeof(Actor), ((Func)HashActor).Method }, { typeof(Player), ((Func)HashPlayer).Method }, { typeof(Target), ((Func)HashTarget).Method }, @@ -62,15 +61,13 @@ namespace OpenRA il.Emit(OpCodes.Ldc_I4, 0x555); il.MarkLabel(l); } - else if (type.HasAttribute()) - il.EmitCall(OpCodes.Call, ((Func)CalculateSyncHash).Method, null); else if (type != typeof(int)) throw new NotImplementedException("SyncAttribute on member of unhashable type: {0}".F(type.FullName)); il.Emit(OpCodes.Xor); } - public static Func GenerateHashFunc(Type t) + static Func GenerateHashFunc(Type t) { var d = new DynamicMethod("hash_{0}".F(t.Name), typeof(int), new Type[] { typeof(object) }, t); var il = d.GetILGenerator(); @@ -116,14 +113,6 @@ namespace OpenRA return ((i2.X * 5) ^ (i2.Y * 3)) / 4; } - public static int HashTDict(TypeDictionary d) - { - var ret = 0; - foreach (var o in d) - ret += CalculateSyncHash(o); - return ret; - } - public static int HashActor(Actor a) { if (a != null) @@ -152,7 +141,7 @@ namespace OpenRA return (int)(t.FrozenActor.Actor.ActorID << 16) * 0x567; case TargetType.Terrain: - return Hash(t.CenterPosition); + return HashUsingHashCode(t.CenterPosition); default: case TargetType.Invalid: @@ -160,7 +149,7 @@ namespace OpenRA } } - public static int Hash(T t) + public static int HashUsingHashCode(T t) { return t.GetHashCode(); } diff --git a/OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs b/OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs index f50eb657cc..54176d24e7 100644 --- a/OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs +++ b/OpenRA.Mods.Common/Traits/Player/MissionObjectives.cs @@ -63,7 +63,7 @@ namespace OpenRA.Mods.Common.Traits { var hash = 0; foreach (var objective in objectives) - hash ^= Sync.Hash(objective.State); + hash ^= Sync.HashUsingHashCode(objective.State); return hash; } } From dc375744949a73778c9433a944c9c63984f4bb01 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Sat, 30 Jan 2016 00:36:20 +0000 Subject: [PATCH 2/2] Cache sync hash functions per actor for faster sync calculations. Caching the result of the function lookup allows the actor to calculate all the sync hashes for its syncable traits faster as it does not need to repeat the lookup each time. --- OpenRA.Game/Actor.cs | 15 +++++++++++++++ OpenRA.Game/Network/SyncReport.cs | 30 ++++++++++++++---------------- OpenRA.Game/Sync.cs | 18 ++++++++++++------ OpenRA.Game/World.cs | 9 +++++---- 4 files changed, 46 insertions(+), 26 deletions(-) diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index bf211ac472..869cc6ab65 100644 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -24,6 +24,13 @@ namespace OpenRA { public sealed class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding, IEquatable, IDisposable { + internal struct SyncHash + { + public readonly ISync Trait; + public readonly int Hash; + public SyncHash(ISync trait, int hash) { Trait = trait; Hash = hash; } + } + public readonly ActorInfo Info; public readonly World World; @@ -62,6 +69,8 @@ namespace OpenRA } } + internal IEnumerable SyncHashes { get; private set; } + readonly IFacing facing; readonly IHealth health; readonly IRenderModifier[] renderModifiers; @@ -112,6 +121,12 @@ namespace OpenRA visibilityModifiers = TraitsImplementing().ToArray(); defaultVisibility = Trait(); Targetables = TraitsImplementing().ToArray(); + + SyncHashes = + TraitsImplementing() + .Select(sync => Pair.New(sync, Sync.GetHashFunction(sync))) + .ToArray() + .Select(pair => new SyncHash(pair.First, pair.Second(pair.First))); } Rectangle DetermineBounds() diff --git a/OpenRA.Game/Network/SyncReport.cs b/OpenRA.Game/Network/SyncReport.cs index c3a2f0fbd3..2917ba9b78 100644 --- a/OpenRA.Game/Network/SyncReport.cs +++ b/OpenRA.Game/Network/SyncReport.cs @@ -63,31 +63,29 @@ namespace OpenRA.Network report.SyncedRandom = orderManager.World.SharedRandom.Last; report.TotalCount = orderManager.World.SharedRandom.TotalCount; report.Traits.Clear(); - foreach (var a in orderManager.World.ActorsWithTrait()) - { - var sync = Sync.CalculateSyncHash(a.Trait); - if (sync != 0) - report.Traits.Add(new TraitReport() - { - ActorID = a.Actor.ActorID, - Type = a.Actor.Info.Name, - Owner = (a.Actor.Owner == null) ? "null" : a.Actor.Owner.PlayerName, - Trait = a.Trait.GetType().Name, - Hash = sync, - NamesValues = DumpSyncTrait(a.Trait) - }); - } + foreach (var actor in orderManager.World.ActorsHavingTrait()) + foreach (var syncHash in actor.SyncHashes) + if (syncHash.Hash != 0) + report.Traits.Add(new TraitReport() + { + ActorID = actor.ActorID, + Type = actor.Info.Name, + Owner = (actor.Owner == null) ? "null" : actor.Owner.PlayerName, + Trait = syncHash.Trait.GetType().Name, + Hash = syncHash.Hash, + NamesValues = DumpSyncTrait(syncHash.Trait) + }); foreach (var e in orderManager.World.Effects) { var sync = e as ISync; if (sync != null) { - var hash = Sync.CalculateSyncHash(sync); + var hash = Sync.Hash(sync); if (hash != 0) report.Effects.Add(new EffectReport() { - Name = sync.ToString().Split('.').Last(), + Name = sync.GetType().Name, Hash = hash, NamesValues = DumpSyncTrait(sync) }); diff --git a/OpenRA.Game/Sync.cs b/OpenRA.Game/Sync.cs index 85afe84109..733be00eae 100644 --- a/OpenRA.Game/Sync.cs +++ b/OpenRA.Game/Sync.cs @@ -26,14 +26,20 @@ namespace OpenRA public static class Sync { - static Cache> hashFuncCache = new Cache>(t => GenerateHashFunc(t)); + static readonly ConcurrentCache> HashFunctions = + new ConcurrentCache>(GenerateHashFunc); - public static int CalculateSyncHash(object obj) + internal static Func GetHashFunction(ISync sync) { - return hashFuncCache[obj.GetType()](obj); + return HashFunctions[sync.GetType()]; } - static Dictionary hashFunctions = new Dictionary() + internal static int Hash(ISync sync) + { + return GetHashFunction(sync)(sync); + } + + static readonly Dictionary CustomHashFunctions = new Dictionary() { { typeof(int2), ((Func)HashInt2).Method }, { typeof(CPos), ((Func)HashCPos).Method }, @@ -50,8 +56,8 @@ namespace OpenRA static void EmitSyncOpcodes(Type type, ILGenerator il) { - if (hashFunctions.ContainsKey(type)) - il.EmitCall(OpCodes.Call, hashFunctions[type], null); + if (CustomHashFunctions.ContainsKey(type)) + il.EmitCall(OpCodes.Call, CustomHashFunctions[type], null); else if (type == typeof(bool)) { var l = il.DefineLabel(); diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 1a9149346d..b37f5c4448 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -341,18 +341,19 @@ namespace OpenRA // hash all the actors foreach (var a in Actors) - ret += n++ * (int)(1 + a.ActorID) * Sync.CalculateSyncHash(a); + ret += n++ * (int)(1 + a.ActorID) * Sync.HashActor(a); // hash all the traits that tick - foreach (var x in ActorsWithTrait()) - ret += n++ * (int)(1 + x.Actor.ActorID) * Sync.CalculateSyncHash(x.Trait); + foreach (var actor in ActorsHavingTrait()) + foreach (var syncHash in actor.SyncHashes) + ret += n++ * (int)(1 + actor.ActorID) * syncHash.Hash; // TODO: don't go over all effects foreach (var e in Effects) { var sync = e as ISync; if (sync != null) - ret += n++ * Sync.CalculateSyncHash(sync); + ret += n++ * Sync.Hash(sync); } // Hash the shared rng