From 3511fbbf2ca92425d39ce47411f08471183b8003 Mon Sep 17 00:00:00 2001 From: RoosterDragon Date: Tue, 16 Dec 2014 18:39:02 +0000 Subject: [PATCH] Generate sync reports faster by delaying ToString calls until it is written to disk. Values for the report are generated by calling ToString on members, we avoid calling this on value types for performance. By instead just copying the value we can delay calling ToString until later and avoid spending time and memory creating strings that usually go unused. --- OpenRA.Game/Network/SyncReport.cs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/OpenRA.Game/Network/SyncReport.cs b/OpenRA.Game/Network/SyncReport.cs index e500576903..20d244e265 100644 --- a/OpenRA.Game/Network/SyncReport.cs +++ b/OpenRA.Game/Network/SyncReport.cs @@ -17,7 +17,7 @@ using OpenRA.Primitives; namespace OpenRA.Network { - using NamesValuesPair = Pair; + using NamesValuesPair = Pair; class SyncReport { @@ -35,10 +35,10 @@ namespace OpenRA.Network TypeInfo typeInfo; lock (typeInfoCache) typeInfo = typeInfoCache[type]; - var values = new string[typeInfo.Names.Length]; + var values = new object[typeInfo.Names.Length]; var index = 0; - foreach (var func in typeInfo.MemberToStringFunctions) + foreach (var func in typeInfo.SerializableCopyOfMemberFunctions) values[index++] = func(sync); return Pair.New(typeInfo.Names, values); @@ -166,7 +166,7 @@ namespace OpenRA.Network static ParameterExpression syncParam = Expression.Parameter(typeof(ISync), "sync"); static ConstantExpression nullString = Expression.Constant(null, typeof(string)); - public readonly Func[] MemberToStringFunctions; + public readonly Func[] SerializableCopyOfMemberFunctions; public readonly string[] Names; public TypeInfo(Type type) @@ -182,14 +182,25 @@ namespace OpenRA.Network "Invalid Property: " + prop.DeclaringType.FullName + "." + prop.Name); var sync = Expression.Convert(syncParam, type); - MemberToStringFunctions = fields - .Select(fi => MemberToString(Expression.Field(sync, fi), fi.FieldType, fi.Name)) - .Concat(properties.Select(pi => MemberToString(Expression.Property(sync, pi), pi.PropertyType, pi.Name))) + 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))) .ToArray(); Names = fields.Select(fi => fi.Name).Concat(properties.Select(pi => pi.Name)).ToArray(); } + static Func SerializableCopyOfMember(MemberExpression getMember, Type memberType, string name) + { + if (memberType.IsValueType) + { + // We can get a copy just by accessing the member since it is a value type. + var boxedCopy = Expression.Convert(getMember, typeof(object)); + return Expression.Lambda>(boxedCopy, name, new[] { syncParam }).Compile(); + } + return MemberToString(getMember, memberType, name); + } + static Func MemberToString(MemberExpression getMember, Type memberType, string name) { // The lambda generated is shown below. @@ -197,10 +208,13 @@ namespace OpenRA.Network var toString = memberType.GetMethod("ToString", Type.EmptyTypes); Expression getString; if (memberType.IsValueType) + { + // (ISync sync) => { return ((TSync)sync).Foo.ToString(); } getString = Expression.Call(getMember, toString); + } else { - // (ISync sync) => { var foo = ((TSync)sync).Foo; return foo == null ? null : foo.ToString()); } + // (ISync sync) => { var foo = ((TSync)sync).Foo; return foo == null ? null : foo.ToString(); } var memberVariable = Expression.Variable(memberType, getMember.Member.Name); var assignMemberVariable = Expression.Assign(memberVariable, getMember); var member = Expression.Block(new[] { memberVariable }, assignMemberVariable);