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.
This commit is contained in:
RoosterDragon
2014-12-16 18:39:02 +00:00
parent f186f22bfc
commit 3511fbbf2c

View File

@@ -17,7 +17,7 @@ using OpenRA.Primitives;
namespace OpenRA.Network
{
using NamesValuesPair = Pair<string[], string[]>;
using NamesValuesPair = Pair<string[], object[]>;
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<ISync, string>[] MemberToStringFunctions;
public readonly Func<ISync, object>[] 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<ISync, object> 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<Func<ISync, object>>(boxedCopy, name, new[] { syncParam }).Compile();
}
return MemberToString(getMember, memberType, name);
}
static Func<ISync, string> 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);