【问题标题】:Why is JIT_MethodAccessAllowedBySecurity taking so much time?为什么 JIT_MethodAccessAllowedBySecurity 需要这么多时间?
【发布时间】:2011-03-30 20:18:56
【问题描述】:

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

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

我们最近开始使用我们的引擎处理一些大容量的滴答数据,我们发现这些编译的表达式树的速度是一个真正的瓶颈 - 重新计算所有这些列时速度非常慢苍蝇。使用内置的 Visual Studio 2010 探查器对其进行分析,发现我们一半的执行时间都花在了 clr.dll 中,在一个名为 JIT_MethodAccessAllowedBySecurity 的方法中。

粗略的谷歌搜索这个字符串没有产生任何结果,所以我想知道是否有人可以告诉我这个方法是什么,是否有办法让它不占用我所有的周期?也许有一种方法可以编译此代码并明确授予它执行任何操作的权限,以便 clr 可以停止这些检查?也许表达式树引擎正在生成的临时程序集没有完全信任?

无论如何,我很茫然,我很想知道过去是否有任何其他 StackOverflow'ers 遇到过这个问题。提前致谢!

【问题讨论】:

  • 您能发布将您带到 JIT_MethodAccessAllowedBySecurity 的堆栈跟踪吗?
  • @NightDweller - 在内置的 VS2010 分析器中,它说 JIT_MethodAccessAllowedBySecurity 在我们调用已编译委托时发生(而不是在已编译委托主体的某个位置)。看起来 Jethro 走在了正确的轨道上,我们必须想办法在委托本身上设置属性。

标签: c# .net expression-trees codedom


【解决方案1】:

解决方案是使用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 调用并在我们的计算引擎中加速超过 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 倍以上。感谢大家的帮助,我希望这可以为其他人节省一些周期。

【讨论】:

  • @Ivilnis - 非常好 - 这个解决方案对我来说很有帮助。希望我能更多地了解为什么它是必要的。
  • @lvilnis 可以确认这是在广泛使用表达式树时仍然适用于 NET45 的魔术包装器。使用分析器时,我有 30% 的 CPU 时间花在 JIT_MemberAccessCheck 上。应用此修复程序后,它就消失了,代码速度提高了 30%。
【解决方案2】:

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

CAS 是基于汇编的。当你编码时 调用受保护的方法,.NET 框架运行时检查您的 组装,看看它是否已被授予 所需的或更多权限 方法。 .NET 框架运行时 然后遍历堆栈检查每个 在堆栈中组装这些 授权。如果一个组件没有 拥有所有必需的权限, 引发了一个安全异常,并且 代码已运行。

以下是我认为您的代码发生的情况。

... 发生堆栈遍历和策略 检查是执行的,每次 方法被调用。这是一个特殊的 类中组件的问题 图书馆,可以称为许多 次。在这种情况下,您可以使用 链接请求以表明 权限集检查在 在链接时作为 JIT 的一部分 并发症的过程。为此,您 使用 a 装饰方法 具有 值的参数 SecurityAction.LinkDemand.

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

问候

【讨论】:

  • 首先,感谢您的帮助,Jethro。不幸的是,SecurityAction.LinkDemand(以及一般 CAS 的大部分)似乎已在 .NET 4.0 中被删除 - 在 VS 中,当我将鼠标悬停在 SecurityAction.LinkDemand 上时,它会显示“不要在 .NET 4.0 中使用”。我认为你在这个想法上是正确的。我将开始研究 LinkDemand 的 .NET 4.0 替代品是什么,以及 CAS 是否仍然以这种方式工作。随之而来的另一个问题是如何为表达式树生成的方法添加属性。
  • System.Linq.Expressions.LambdaExpression.CompileToMethod(System.Reflection.Emit.MethodBuilder method) 看起来可以解决问题!
猜你喜欢
  • 2015-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-12
  • 1970-01-01
  • 2018-01-28
  • 1970-01-01
相关资源
最近更新 更多