我正在开发一个C#应用程序,它允许用户基本上导入数据表,然后用迷你语言输入他们自己的公式,从底层数据中计算新列。
这些公式被编译到引擎中的LINQ表达式树中,然后.NET 4.0表达式树库可能会编译成IL,因此可以执行它们。
我们最近开始使用我们的引擎获取一些高容量的滴答数据,我们发现这些编译的表达式树的速度是一个真正的瓶颈 - 当重新计算所有这些列时速度相当慢苍蝇使用内置的Visual Studio 2010探查器进行攻击,可以看出,我们所有执行时间的一半都花在clr.dll中,名为JIT_MethodAccessAllowedBySecurity。
粗略的谷歌搜索这个字符串并没有产生什么,所以我想知道是否有人可以告诉我这个方法是什么,以及是否有办法让它不吃掉我所有的周期?也许有一种方法可以编译这段代码并明确授予它做任何想做的事情,这样clr可以停止这些检查吗?也许表达式树引擎生成的临时程序集没有完全信任?无论如何,我几乎感到茫然,我很想知道过去是否有其他任何StackOverflower遇到过这个问题。提前谢谢!
答案 0 :(得分:12)
解决方案是使用LambdaExpression.CompileToMethod(MethodBuilder method)代替LambdaExpression.Compile()。
我认为Jethro在假定CAS参与其中时走在了正确的轨道上。在测试中,当我使用表达式树调用未在生成的程序集中动态定义的函数时(即使用Expression.Call调用库方法而不是生成的代码片段),探查器才开始显示对JIT_MethodAccessAllowedBySecurity的调用。表明减速是由CAS检查我生成的代码可以访问它正在调用的方法引起的。似乎通过对我希望调用的函数应用声明性安全性修改,我可以避免这种开销。
不幸的是,通过使用声明性安全性(PermissionSet,SecurityAction.LinkDemand等),我无法摆脱JIT_MethodAccessAllowedBySecurity开销。有一次,我的项目中的每个方法都标有[PermissionSet(SecurityAction.LinkDemand,Unrestricted = true)],没有结果。
幸运的是,在寻找为生成的委托添加属性的方法时,我偶然发现了解决方案 - 使用MethodBuilder编译表达式树而不是内置的LambdaExpression.Compile方法。
我已经包含了替换.Compile()的代码,导致在我们的计算引擎中消除了JIT_MethodAccessAllowedBySecurity调用和> 2x加速:
// T must be of delegate type (Func<T>, Func<T1, T2>, etc.)
public static T GetCompiledDelegate<T>(Expression<T> expr)
{
var assemblyName = new AssemblyName("DelegateHostAssembly") { Version = new Version("1.0.0.0") };
var assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(
assemblyName,
AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("DelegateHostAssembly", "DelegateHostAssembly.dll");
var typeBuilder = moduleBuilder.DefineType("DelegateHostAssembly." + "foo", TypeAttributes.Public);
var methBldr = typeBuilder.DefineMethod("Execute", MethodAttributes.Public | MethodAttributes.Static);
expr.CompileToMethod(methBldr);
Type myType = typeBuilder.CreateType();
var mi = myType.GetMethod("Execute");
// have to box to object because .NET doesn't allow Delegates as generic constraints,
// nor does it allow casting of Delegates to generic type variables like "T"
object foo = Delegate.CreateDelegate(typeof(T), mi);
return (T)foo;
}
使用任何使用表达式树来调用表达式树本身未定义的函数的代码时,此代码的速度始终快2倍。感谢大家的帮助,我希望这可以为其他人节省一些时间。
答案 1 :(得分:6)
我认为这与CAS(代码访问安全性)有关。
CAS是基于程序集的。当你编码 调用受保护的方法.NET 框架运行时检查你的 汇编以查看是否已被授予 一个或多个必要的权限 方法。 .NET Framework rutime 然后走遍堆栈检查每一个 在堆栈中组装这些 ermissions。如果一个组件没有 拥有所有必需的权限, 引发了一个securityexception和 代码运行。
以下是我认为您的代码正在发生的事情。
...发生堆栈行走和策略 每次检查都是预先形成的 方法被调用。这是一个特别的 类中组件的问题 库,可能被称为许多 倍。在这种情况下,你可以使用 链接需求表明 执行权限集检查 在链接时作为JIT的一部分 复杂的过程。要做到这一点,你 使用a来装饰方法 具有的权限属性 值的参数
SecurityAction.LinkDemand
。
我希望这会有所帮助,看起来您需要做的就是设置SecurityAction.LinkDemand
属性。引用的文本来自Microsoft .NET 2.0开发的高级基础。
此致