Files
RobustToolbox/Robust.Shared/Utility/ILGeneratorExt.cs
T
DrSmugleafandGitHub f9cd9ac12a Add serialization copy benchmark, optimize copying and reading with IL generators (#1729)
* Create serialization copy benchmark

* Optimize copy generic method call

Before
|                             Method |         Mean |      Error |     StdDev |       Median |
|----------------------------------- |-------------:|-----------:|-----------:|-------------:|
|                   CreateCopyString |     43.54 ns |   0.918 ns |   0.859 ns |     43.74 ns |
|                  CreateCopyInteger |     19.33 ns |   0.388 ns |   0.860 ns |     19.24 ns |
| CreateCopyDataDefinitionWithString |  3,971.42 ns |  78.974 ns | 213.512 ns |  3,902.54 ns |
|       CreateCopySeedDataDefinition | 49,916.69 ns | 264.992 ns | 221.280 ns | 49,950.53 ns |

After
|                             Method |         Mean |      Error |     StdDev |
|----------------------------------- |-------------:|-----------:|-----------:|
|                   CreateCopyString |     43.15 ns |   0.917 ns |   1.766 ns |
|                  CreateCopyInteger |     17.91 ns |   0.366 ns |   0.305 ns |
| CreateCopyDataDefinitionWithString |    544.20 ns |   8.137 ns |   7.611 ns |
|       CreateCopySeedDataDefinition | 18,233.34 ns | 357.861 ns | 397.761 ns |

* Change populate delegate to use IL

|                       Method |        Mean |     Error |    StdDev |
|----------------------------- |------------:|----------:|----------:|
|                   ReadString |    190.7 ns |   3.48 ns |   5.63 ns |
|                  ReadInteger |    218.8 ns |   4.29 ns |   6.01 ns |
| ReadDataDefinitionWithString |    677.5 ns |  13.34 ns |  24.06 ns |
|       ReadSeedDataDefinition | 11,067.0 ns | 219.19 ns | 260.93 ns |

|                             Method |         Mean |      Error |     StdDev |
|----------------------------------- |-------------:|-----------:|-----------:|
|                   CreateCopyString |     42.69 ns |   0.926 ns |   1.414 ns |
|                  CreateCopyInteger |     18.02 ns |   0.268 ns |   0.237 ns |
| CreateCopyDataDefinitionWithString |    556.80 ns |  11.206 ns |  21.589 ns |
|       CreateCopySeedDataDefinition | 13,797.07 ns | 256.011 ns | 367.163 ns |

* Make copying use IL to get values

|                               Method |        Mean |      Error |     StdDev |
|------------------------------------- |------------:|-----------:|-----------:|
|                     CreateCopyString |    42.55 ns |   0.889 ns |   1.357 ns |
|                    CreateCopyInteger |    18.72 ns |   0.394 ns |   0.740 ns |
|   CreateCopyDataDefinitionWithString |   398.98 ns |   7.853 ns |  12.682 ns |
|         CreateCopySeedDataDefinition | 5,996.60 ns | 118.724 ns | 181.304 ns |
| BaselineCreateCopySeedDataDefinition |   340.85 ns |   6.811 ns |  12.281 ns |

* Rest in peace the fast path
For now

* Fix serialization copying not passing the target ref properly

* Fix field assigners for structs
2021-05-02 12:09:12 +02:00

192 lines
5.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
namespace Robust.Shared.Utility
{
public static class ILGeneratorExt
{
public static RobustILGenerator GetRobustGen(this DynamicMethod dynamicMethod) =>
new(dynamicMethod.GetILGenerator());
public static RobustILGenerator GetRobustGen(this ILGenerator generator) => new (generator);
}
public class RobustILGenerator
{
private ILGenerator _generator;
private List<(object, object?)> _log = new();
private List<(Type, int)> _locals = new();
public RobustILGenerator(ILGenerator generator)
{
_generator = generator;
}
public string[] GetStringLog()
{
var full = _locals.Select(e => $"LOCAL: {e.Item1} at {e.Item2}").ToList();
full.Add("===== CODE =====");
full.AddRange(_log.Select(e => $"{e.Item1} - {e.Item2}"));
return full.ToArray();
}
private void Log(object opcode, object? arg = null)
{
_log.Add((opcode, arg));
}
public virtual LocalBuilder DeclareLocal(Type localType)
{
return DeclareLocal(localType, false);
}
public virtual LocalBuilder DeclareLocal(Type localType, bool pinned)
{
var loc = _generator.DeclareLocal(localType, pinned);
_locals.Add((localType, loc.LocalIndex));
return loc;
}
public virtual void ThrowException([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type excType)
{
//this just calls emit
_generator.ThrowException(excType);
}
public virtual Label DefineLabel()
{
var label = _generator.DefineLabel();
//Log("DefineLabel", label);
return label;
}
public virtual void MarkLabel(Label loc)
{
_generator.MarkLabel(loc);
Log("MarkLabel", loc.GetHashCode());
}
public virtual void Emit(OpCode opcode)
{
_generator.Emit(opcode);
Log(opcode);
}
public virtual void Emit(OpCode opcode, byte arg)
{
_generator.Emit(opcode, arg);
Log(opcode, arg);
}
public void Emit(OpCode opcode, sbyte arg)
{
_generator.Emit(opcode, arg);
Log(opcode, arg);
}
public virtual void Emit(OpCode opcode, short arg)
{
_generator.Emit(opcode, arg);
Log(opcode, arg);
}
public virtual void Emit(OpCode opcode, int arg)
{
_generator.Emit(opcode, arg);
Log(opcode, arg);
}
public virtual void Emit(OpCode opcode, MethodInfo meth)
{
_generator.Emit(opcode, meth);
Log(opcode, meth);
}
// public virtual void EmitCalli(OpCode opcode, CallingConventions callingConvention,
// Type? returnType, Type[]? parameterTypes, Type[]? optionalParameterTypes)
// {
// }
//
// public virtual void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type? returnType, Type[]? parameterTypes)
// {
// }
//
public virtual void EmitCall(OpCode opcode, MethodInfo methodInfo, params Type[]? optionalParameterTypes)
{
_generator.EmitCall(opcode, methodInfo, optionalParameterTypes);
Log(opcode, methodInfo);
}
public virtual void Emit(OpCode opcode, SignatureHelper signature)
{
_generator.Emit(opcode, signature);
Log(opcode, signature);
}
public virtual void Emit(OpCode opcode, ConstructorInfo con)
{
_generator.Emit(opcode, con);
Log(opcode, con);
}
public virtual void Emit(OpCode opcode, Type cls)
{
_generator.Emit(opcode, cls);
Log(opcode, cls);
}
public virtual void Emit(OpCode opcode, long arg)
{
_generator.Emit(opcode, arg);
Log(opcode, arg);
}
public virtual void Emit(OpCode opcode, float arg)
{
_generator.Emit(opcode, arg);
Log(opcode, arg);
}
public virtual void Emit(OpCode opcode, double arg)
{
_generator.Emit(opcode, arg);
Log(opcode, arg);
}
public virtual void Emit(OpCode opcode, Label label)
{
_generator.Emit(opcode, label);
Log(opcode, label.GetHashCode());
}
public virtual void Emit(OpCode opcode, Label[] labels)
{
_generator.Emit(opcode, labels);
Log(opcode, labels);
}
public virtual void Emit(OpCode opcode, FieldInfo field)
{
_generator.Emit(opcode, field);
Log(opcode, field);
}
public virtual void Emit(OpCode opcode, string str)
{
_generator.Emit(opcode, str);
Log(opcode, str);
}
public virtual void Emit(OpCode opcode, LocalBuilder local)
{
_generator.Emit(opcode, local);
Log(opcode, local);
}
}
}