为什么JIT_MethodAccessAllowedBySecurity花了这么多时间?

时间:2011-03-30 20:18:56

标签: c# .net expression-trees codedom

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

这些公式被编译到引擎中的LINQ表达式树中,然后.NET 4.0表达式树库可能会编译成IL,因此可以执行它们。

我们最近开始使用我们的引擎获取一些高容量的滴答数据,我们发现这些编译的表达式树的速度是一个真正的瓶颈 - 当重新计算所有这些列时速度相当慢苍蝇使用内置的Visual Studio 2010探查器进行攻击,可以看出,我们所有执行时间的一半都花在clr.dll中,名为JIT_MethodAccessAllowedBySecurity。

粗略的谷歌搜索这个字符串并没有产生什么,所以我想知道是否有人可以告诉我这个方法是什么,以及是否有办法让它不吃掉我所有的周期?也许有一种方法可以编译这段代码并明确授予它做任何想做的事情,这样clr可以停止这些检查吗?也许表达式树引擎生成的临时程序集没有完全信任?

无论如何,我几乎感到茫然,我很想知道过去是否有其他任何StackOverflower遇到过这个问题。提前谢谢!

2 个答案:

答案 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开发的高级基础。

此致