mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Reduce DynamicMethod use in Serv3
Data definitions created individual read/write methods for every single field that can be serialized. This was extremely inefficient and likely caused lots of overhead. These methods are necessary because, in some cases, we can't directly use expression trees to write fields. But... we can still do it most of the time! So now for most data fields we can avoid a DynamicMethod entirely.
This commit is contained in:
@@ -47,7 +47,7 @@ END TEMPLATE-->
|
||||
|
||||
### Other
|
||||
|
||||
*None yet*
|
||||
* Reduced amount of `DynamicMethod`s used by serialization system. This should improve performance somewhat.
|
||||
|
||||
### Internal
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ internal partial class UserInterfaceManager
|
||||
continue;
|
||||
|
||||
var typeDict = _assignerRegistry.GetOrNew(fieldInfo.FieldType);
|
||||
var assigner = (InternalReflectionUtils.AssignField<UIController, object?>)InternalReflectionUtils.EmitFieldAssigner<UIController>(backingField, true);
|
||||
var assigner = (InternalReflectionUtils.AssignField<UIController, object?>)InternalReflectionUtils.EmitFieldAssigner(typeof(UIController), backingField, true);
|
||||
typeDict.Add(controllerType, assigner);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -476,15 +477,32 @@ namespace Robust.Shared.Serialization.Manager.Definition
|
||||
|
||||
private Expression AssignIfNotDefaultExpression(int i, Expression obj, Expression value)
|
||||
{
|
||||
var assigner = FieldAssigners[i];
|
||||
Expression assignerExpr;
|
||||
|
||||
if (assigner is FieldInfo fieldInfo)
|
||||
assignerExpr = Expression.Assign(Expression.Field(obj, fieldInfo), value);
|
||||
else if (assigner is MethodInfo methodInfo)
|
||||
assignerExpr = Expression.Call(obj, methodInfo, value);
|
||||
else
|
||||
assignerExpr = Expression.Invoke(Expression.Constant(assigner), obj, value);
|
||||
|
||||
return Expression.IfThen(
|
||||
Expression.Not(ExpressionUtils.EqualExpression(
|
||||
Expression.Constant(DefaultValues[i], BaseFieldDefinitions[i].FieldType), value)),
|
||||
Expression.Invoke(Expression.Constant(FieldAssigners[i]), obj, value));
|
||||
assignerExpr);
|
||||
}
|
||||
|
||||
private Expression AccessExpression(int i, Expression obj)
|
||||
{
|
||||
return Expression.Invoke(Expression.Constant(FieldAccessors[i]), obj);
|
||||
var accessor = FieldAccessors[i];
|
||||
if (accessor is FieldInfo fieldInfo)
|
||||
return Expression.Field(obj, fieldInfo);
|
||||
|
||||
if (accessor is MethodInfo methodInfo)
|
||||
return Expression.Call(obj, methodInfo);
|
||||
|
||||
return Expression.Invoke(Expression.Constant(accessor), obj);
|
||||
}
|
||||
|
||||
private Expression IsDefault(int i, Expression left, FieldDefinition fieldDefinition)
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace Robust.Shared.Serialization.Manager.Definition
|
||||
for (var i = 0; i < BaseFieldDefinitions.Length; i++)
|
||||
{
|
||||
var fieldDefinition = BaseFieldDefinitions[i];
|
||||
fieldAssigners[i] = InternalReflectionUtils.EmitFieldAssigner<T>(fieldDefinition.BackingField);
|
||||
fieldAssigners[i] = InternalReflectionUtils.EmitFieldAssigner(typeof(T), fieldDefinition.BackingField);
|
||||
fieldAccessors[i] = InternalReflectionUtils.EmitFieldAccessor(typeof(T), fieldDefinition);
|
||||
|
||||
if (fieldDefinition.Attribute.CustomTypeSerializer != null)
|
||||
|
||||
@@ -39,6 +39,12 @@ internal static class InternalReflectionUtils
|
||||
|
||||
internal static object EmitFieldAccessor(Type obj, FieldDefinition fieldDefinition)
|
||||
{
|
||||
if (fieldDefinition.BackingField is SpecificFieldInfo fieldInfo)
|
||||
return fieldInfo.FieldInfo;
|
||||
|
||||
if (fieldDefinition.BackingField is SpecificPropertyInfo propertyInfo)
|
||||
return propertyInfo.PropertyInfo.GetGetMethod(true) ?? throw new InvalidOperationException("Property has no getter");
|
||||
|
||||
var method = new DynamicMethod(
|
||||
"AccessField",
|
||||
fieldDefinition.BackingField.FieldType,
|
||||
@@ -71,14 +77,29 @@ internal static class InternalReflectionUtils
|
||||
return method.CreateDelegate(typeof(AccessField<,>).MakeGenericType(obj, fieldDefinition.BackingField.FieldType));
|
||||
}
|
||||
|
||||
internal static object EmitFieldAssigner<TObj>(AbstractFieldInfo backingField, bool boxing = false)
|
||||
internal static object EmitFieldAssigner(Type objType, AbstractFieldInfo backingField, bool boxing = false)
|
||||
{
|
||||
if (!boxing)
|
||||
{
|
||||
if (backingField is SpecificFieldInfo { FieldInfo.IsInitOnly: false } fieldInfo)
|
||||
return fieldInfo.FieldInfo;
|
||||
|
||||
if (backingField is SpecificPropertyInfo propertyInfo)
|
||||
{
|
||||
if (propertyInfo.TryGetBackingField(out var propertyBackingField) && !propertyBackingField.FieldInfo.IsInitOnly)
|
||||
return propertyBackingField.FieldInfo;
|
||||
|
||||
if (propertyInfo.PropertyInfo.GetSetMethod(true) is { } setMethod)
|
||||
return setMethod;
|
||||
}
|
||||
}
|
||||
|
||||
var fieldType = backingField.FieldType;
|
||||
|
||||
var method = new DynamicMethod(
|
||||
"AssignField",
|
||||
typeof(void),
|
||||
new[] {typeof(TObj).MakeByRefType(), boxing ? typeof(object) : fieldType},
|
||||
new[] {objType.MakeByRefType(), boxing ? typeof(object) : fieldType},
|
||||
true);
|
||||
|
||||
method.DefineParameter(1, ParameterAttributes.Out, "target");
|
||||
@@ -88,7 +109,7 @@ internal static class InternalReflectionUtils
|
||||
|
||||
generator.Emit(OpCodes.Ldarg_0);
|
||||
|
||||
if(!typeof(TObj).IsValueType)
|
||||
if(!objType.IsValueType)
|
||||
generator.Emit(OpCodes.Ldind_Ref);
|
||||
|
||||
generator.Emit(OpCodes.Ldarg_1);
|
||||
@@ -102,6 +123,6 @@ internal static class InternalReflectionUtils
|
||||
|
||||
generator.Emit(OpCodes.Ret);
|
||||
|
||||
return method.CreateDelegate(typeof(AssignField<,>).MakeGenericType(typeof(TObj), boxing ? typeof(object) : fieldType));
|
||||
return method.CreateDelegate(typeof(AssignField<,>).MakeGenericType(objType, boxing ? typeof(object) : fieldType));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user