Почему JIT_MethodAccessAllowedBySecurity занимает так много времени? - PullRequest
9 голосов
/ 31 марта 2011

Я работаю над приложением C #, которое позволяет пользователям в основном импортировать таблицы данных, а затем вводить свои собственные формулы на мини-языке для вычисления новых столбцов из базовых данных.

Эти формулы скомпилированы в деревья выражений LINQ в механизме, который библиотека дерева выражений .NET 4.0, вероятно, затем скомпилирует в IL, чтобы их можно было выполнить.

Мы недавно начали использовать наш движок для некоторых тиковых данных большого объема, и мы находим скорость этих скомпилированных деревьев выражений реальным узким местом - скорость довольно медленная при пересчете всех этих столбцов на муха. Удар по нему с помощью встроенного в Visual Studio 2010 профилировщика показывает, что половина нашего времени выполнения тратится в clr.dll, в методе с именем JIT_MethodAccessAllowedBySecurity.

Беглое гугление этой строки ничего не дало, поэтому мне интересно, есть ли кто-нибудь, кто может сказать мне, что это за метод, и есть ли способ не дать ему съесть все мои циклы? Может быть, есть способ скомпилировать этот код и явно дать ему разрешение делать все, что он хочет, чтобы clr мог остановить эти проверки? Возможно, временные сборки, генерируемые механизмом дерева выражений, не имеют полного доверия?

Во всяком случае, я в значительной степени растерян, и мне очень интересно узнать, сталкивались ли другие StackOverflow'ы с этой проблемой в прошлом. Заранее спасибо!

Ответы [ 2 ]

12 голосов
/ 13 апреля 2011

Решение состоит в том, чтобы использовать LambdaExpression.CompileToMethod (метод MethodBuilder) вместо LambdaExpression.Compile () .

Я думаю, что Джетро был на правильном пути, когда утверждал, что CAS был замешан. В ходе тестирования профилировщик начал показывать вызовы JIT_MethodAccessAllowedBySecurity, когда я использовал деревья выражений для вызова функций, которые не были определены динамически в сгенерированной сборке (т. Е. С помощью Expression.Call для вызова метода библиотеки, а не фрагмента сгенерированного кода). предполагает, что замедление было вызвано проверкой CAS, что мой сгенерированный код имел доступ к методам, которые он вызывал. Из этого следует, что, применяя декларативные модификации безопасности к функциям, которые я хотел вызвать, я мог бы избежать этих издержек.

К сожалению, я не смог избавиться от издержек JIT_MethodAccessAllowedBySecurity при любом использовании декларативной безопасности (PermissionSet, SecurityAction.LinkDemand и тому подобное). В какой-то момент у меня был буквально каждый метод в моем проекте, помеченный [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 раза быстрее при использовании любого кода, который использует деревья выражений для вызова функций, которые сами не определены деревьями выражений. Спасибо всем за помощь, и я надеюсь, что это сэкономит кому-то еще несколько циклов.

6 голосов
/ 10 апреля 2011

Я думаю, что это как-то связано с CAS (защита доступа к коду).

CAS основан на сборке.Когда код вызывает вызываемый защищенный метод, среда выполнения .NET проверяет вашу сборку, чтобы узнать, была ли она предоставлена ​​или для этого метода необходимы дополнительные разрешения.Затем .NET Framework rutime обходит стек, чтобы проверить наличие ошибок в каждой сборке в стеке.Если одна сборка не имеет всех необходимых разрешений, возникает исключение безопасности и запускается код.

Ниже показано, что происходит с вашим кодом.

... происходит обход стека, и проверка политики выполняется каждый раз при вызове метода.Это особая проблема для компонентов в библиотеке классов, которая может вызываться много раз.В этой ситуации вы можете использовать требование ссылки, чтобы указать, что проверка набора разрешений выполняется во время соединения как часть процесса усложнения JIT.Для этого вы декорируете метод с помощью атрибута полномочий, у которого есть параметр со значением SecurityAction.LinkDemand.

Надеюсь, это поможет, похоже, все, что вам нужно сделать, это установить SecurityAction.LinkDemand атрибут.Цитируемый текст взят из расширенных основ разработки Microsoft .NET 2.0.

С уважением

...