我正致力于动态实例化类的自动化。
我决定编写一个生成Func
的表达式树,可以为我实例化我的类。但是,我注意到Func
的性能降低了3倍,而不是简单地使用new
。
根据我对表达树和调用函数的了解,性能差异应该几乎不存在(可能是20-30%,但远不及慢3倍)
首先,这是我正在构建的表达
public Expression<Func<A1, T>> BuildLambda<T, A1>(string param1Name)
{
var createdType = typeof(T);
var param = Expression.Parameter(typeof(A1), param1Name);
var ctor = Expression.New(createdType);
var prop = createdType.GetProperty(param1Name);
var displayValueAssignment = Expression.Bind(prop, param);
var memberInit = Expression.MemberInit(ctor, displayValueAssignment);
return
Expression.Lambda<Func<A1, T>>(memberInit, param);
}
然后我继续编译它(我只做一次)
var c1 = mapper.BuildLambda<Class1, int>("Id").Compile();
然后我像这样调用我的Func
var result = c1.Invoke(5);
当我把这最后一部分放在循环中并将其与
之类的东西进行比较时var result = new Class1() { Id = 5 };
我做了几个测试,比较两者的性能,这就是我最终的结果:
100,000 Iterations - new: 0ms. | Func 2ms.
600,000 Iterations - new: 5ms. | Func 14ms.
3,100,000 Iterations - new: 24ms. | Func 74ms.
15,600,000 Iterations - new: 118ms. | Func 378ms.
78,100,000 Iterations - new: 597ms. | Func 1767ms.
正如您所看到的,Func.Invoke()
与使用new
进行实例化相比,大约 2.5 - 3倍。
有没有人有任何关于如何改进这个的提示? (我不介意使用纯粹的反射,因为我设法获得更好的性能)
*对于想要在此处测试的人,我的设置为https://pastebin.com/yvMLqZ2t
答案 0 :(得分:2)
在阅读了评论中的所有帖子后,我想出了这个想法:当你创建一个DynamicMethod
而不是表达式树并且你将它逻辑地分配给当前执行代码的模块时,你不应该得到这个开销。
我认为(或至少希望)您正在寻找关于一般概念的改进选项,而不是基于表达式树的版本,因此我将其作为改进选项发布:)
所以我尝试了这段代码:
public static Func<A1, T> BuildLambda<A1, T>(string propertyName)
{
// This is where the magic happens with the last parameter!!
DynamicMethod dm = new DynamicMethod("Create", typeof(T), new Type[] { typeof(A1) }, typeof(Program).Module);
// Everything else is just generating IL-code at runtime to create the class and set the property
var setter = typeof(T).GetProperty(propertyName).SetMethod;
var generator = dm.GetILGenerator();
var local = generator.DeclareLocal(typeof(T));
generator.Emit(OpCodes.Newobj, typeof(Class1).GetConstructor(Type.EmptyTypes));
generator.Emit(OpCodes.Stloc, local);
generator.Emit(OpCodes.Ldloc, local);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Call, setter);
generator.Emit(OpCodes.Ldloc, local);
generator.Emit(OpCodes.Ret);
return (Func<A1, T>)dm.CreateDelegate(typeof(Func<A1, T>));
}
在我的机器上,这些代表的执行速度比手写代码慢了1.8倍,没有指定属性。不是1.5,但至少我不必在我的代码中包含我不完全理解的程序集范围的属性:)
请注意,如果省略DynamicMethod
构造函数的最后一个参数,则生成的代码仍会得到更慢的结果。
修改强>
我偶然发现了这篇博文,它提出了同样的问题并给出了相同的解决方案:
答案 1 :(得分:0)
让我尝试不同的东西。你可能会做的就是讨好:
Func<TArg, TRes> BuildFuncFor<TClass, TArg, TRes>(Func<TClass> typeCreator, Action<TArg, TClass> argumentAssigner) {
return arg => {
var type = typeCreator();
argumentAssigner(arg, type);
return type;
}
}
然后,可以应用相同的currying方法来提供两个func的默认/动态实现。典型的typeCreator
类似于Activator.Create(...)
。根据您的逻辑,可能需要更多功能;例如:Func<object[]> constructorArgumentsSupplier
。同样适用于将给定值分配给给定属性:良好的旧反射:完全符合WPF的方式。
但是大多数1)可能只为某种类型创建一次并缓存以供进一步使用; 2)预编译而不是依赖表达式,这是一种痛苦。