为什么JIT_MethodAccessAllowedBySecurity需要这么多时间?

lvi*_*nis 9 .net c# codedom expression-trees

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

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

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

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

无论如何,我几乎不知所措,我很想知道其他任何StackOverflow'ers过去是否遇到过这个问题.提前致谢!

lvi*_*nis 12

解决方案是使用LambdaExpression.CompileToMethod(MethodBuilder方法)而不是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;
}
Run Code Online (Sandbox Code Playgroud)

当使用任何使用表达式树来调用表达式树本身不定义的函数的代码时,此代码的速度始终> 2倍.感谢大家的帮助,我希望这可以为其他人节省一些时间.


Jet*_*hro 6

我认为这与CAS(代码访问安全性)有关.

CAS是基于装配的.当您编写调用受保护方法的代码时,.NET框架运行时会检查您的程序集以查看它是否已被授予该方法所需的权限或更多权限.然后.NET Framework rutime遍历堆栈以检查堆栈中的每个程序集是否存在这些错误.如果一个程序集没有所有必需的权限,则会引发securityexception并运行代码.

以下是我认为您的代码正在发生的事情.

...每次调用方法时都会执行堆栈遍历并执行策略检查.对于类库中的组件,这是一个特殊问题,可能会多次调用.在这种情况下,您可以使用链接请求来指示在链接时执行权限集检查,作为JIT复杂过程的一部分.为此,您可以使用具有值参数的权限属性来修饰方法 SecurityAction.LinkDemand.

我希望这会有所帮助,看起来您需要做的就是设置SecurityAction.LinkDemand属性.引用的文本来自Microsoft .NET 2.0开发的高级基础.

问候