我正在开发一个C#应用程序,该应用程序允许用户基本上导入数据表,然后以迷你语言输入自己的公式以从基础数据计算新列。

这些公式在引擎中被编译为LINQ表达式树,.NET 4.0表达式树库随后可能会编译为IL,以便可以执行它们。

我们最近开始使用我们的引擎处理一些大批量报价数据,并且发现这些编译后的表达式树的速度是一个真正的瓶颈-快速重新计算所有这些列时的速度相当慢。使用内置的Visual Studio 2010探查器进行分析可以发现,使用一种名为JIT_MethodAccessAllowedBySecurity的方法,我们执行时间的一半都花在了clr.dll中。

该字符串的Cursory谷歌搜索没有产生任何结果,所以我想知道是否有人可以告诉我该方法是什么,以及是否有一种方法可以防止它耗尽我的所有精力?也许有一种方法可以编译此代码,并明确授予它执行所需操作的权限,以便clr可以停止这些检查?也许表达式树引擎生成的临时程序集不完全信任?

无论如何,我几乎茫然不知所措,并且我非常想知道过去是否有其他StackOverflow'ers遇到过此问题。提前致谢!

最佳答案

解决方案是使用LambdaExpression.CompileToMethod(MethodBuilder method)而不是LambdaExpression.Compile()

我认为,杰瑟罗(Jethro)认为CAS参与其中时,正处于正确的轨道上。在测试中,当我使用表达式树来调用在生成的程序集中未动态定义的函数时(例如,使用Expression.Call调用库方法而不是一段生成的代码),探查器仅开始显示对JIT_MethodAccessAllowedBySecurity的调用。这表明,速度下降是由CAS检查我生成的代码是否可以访问其调用的方法引起的。看来可以通过对我希望调用的函数应用声明性安全性修改,可以避免这种开销。

不幸的是,我无法通过使用声明式安全性(PermissionSet,SecurityAction.LinkDemand等)来摆脱JIT_MethodAccessAllowedBySecurity开销。有一次,我在项目中几乎所有方法都标记为[PermissionSet(SecurityAction.LinkDemand,Unrestricted = true)],但没有结果。

幸运的是,在寻找向生成的委托(delegate)中添加属性的方法时,我偶然发现了该解决方案-使用MethodBuilder来编译表达式树,而不是内置的LambdaExpression.Compile方法。

我已经包含了替换.Compile()的代码,从而消除了JIT_MethodAccessAllowedBySecurity调用,并在我们的计算引擎中实现了> 2倍的加速:

// 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倍以上。感谢大家的帮助,我希望这可以节省其他人的时间。

关于c# - 为什么JIT_MethodAccessAllowedBySecurity需要这么多时间?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/5491837/

10-17 00:42