解决方案是使用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 倍以上。感谢大家的帮助,我希望这可以为其他人节省一些周期。