Open arontsang opened 3 years ago
Hi @arontsang , you are right. Actual benchmarks show that performance difference is big enough.
| Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD |
|----------- |-------------- |-------------- |----------:|----------:|----------:|------:|--------:|
| Reflection | .NET 4.7.2 | .NET 4.7.2 | 1.1873 ns | 0.0425 ns | 0.0398 ns | 1.00 | 0.00 |
| Reflection | .NET Core 3.1 | .NET Core 3.1 | 1.1554 ns | 0.0447 ns | 0.0418 ns | 0.97 | 0.05 |
| | | | | | | | |
| Cache | .NET 4.7.2 | .NET 4.7.2 | 0.1364 ns | 0.0182 ns | 0.0162 ns | 1.00 | 0.00 |
| Cache | .NET Core 3.1 | .NET Core 3.1 | 0.1495 ns | 0.0200 ns | 0.0178 ns | 1.11 | 0.20 |
Please feel free to contribute!
I tried to contribute this feature, but unfortunately, it is a little too complicated. I found that my FieldAccessors for classes nested in a generic class failed
public Foo<T>
{
public class Bar
{
public void Method() => {}
// Could not access the MethodInfo Cache Field.
}
}
Sorry for delay, was on vacation)
Yes - generics are tricky, half of all bugs in this project are generics related.
For example you can take a look at here. In short, you need not to call FieldDef itself, but rather FieldRef with DeclaringType=generic TypeRef of class where field resides.
But if it doesn't help I will definitely add this feature in future.
Here is my attempt at this.
As I said before, it is failing on generics.
At present, things that aren't done:
AdviceWeaveProcessBase<TEffect>
protected virtual Cut LoadMethodArgument(Cut pc, AdviceArgument parameter)
{
var methodInfoName = _method.Name + "_MethodInfo";
FieldDefinition methodCache = _type.Fields.FirstOrDefault(x => x.Name == methodInfoName);
if(methodCache == null)
{
methodCache = new FieldDefinition(
methodInfoName,
FieldAttributes.Public | FieldAttributes.Static,
_type.Module.ImportReference(WellKnownTypes.MethodBase));
_type.Fields.Add(methodCache);
MethodDefinition GetTypeStaticConstructor()
{
var staticConstructor = _type.GetStaticConstructor();
if (staticConstructor != null) return staticConstructor;
var methodAttributes = MethodAttributes.Static | MethodAttributes.HideBySig |
MethodAttributes.SpecialName | MethodAttributes.RTSpecialName;
staticConstructor = new MethodDefinition(".cctor", methodAttributes,
_type.Module.ImportReference(StandardTypes.Void));
staticConstructor.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
staticConstructor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
_type.Methods.Add(staticConstructor);
return staticConstructor;
}
GetTypeStaticConstructor()
.Body
.BeforeExit(cut =>
{
return cut.Here(x => x.MethodOf(_method).Cast(StandardTypes.Object, WellKnownTypes.MethodBase))
.Write(OpCodes.Stsfld, new FieldReference(methodInfoName, _type));
});
}
return pc
.Write(OpCodes.Ldsfld, new FieldReference(methodInfoName, _type));
}
Hi @arontsang Well done! Could you make it a PR ? Regarding missing things:
If Aspect around the method, it will generate code like:
private object __a$_around_Method_100663472_w_0(object[] A_1) { return this.Aspect.Around(new Func<object[], object>(this.__a$_around_Method_100663472_u), A_1); }
The
new Func<>()
method will be exectued each time for create Delegate. How about use a static field cache Delegate,static Func<@classType, object[], object> Func = this.__a$_around_Method_100663472_u; private object __a$_around_Method_100663472_w_0(object[] A_1) { return this.Aspect.Around(Func, this, A_1); } private static object __a$_around_Method_100663472_u(@classType instance, object[] A_1) { return instance.__a$_around_Method_100663472_o((int)A_1[0]); }
Do you think it's worth it? @jerviscui
There are improvements. So cool!
A reflection call on each invocation limits performance when using the metadata argument.