Pull to refresh

Comments 13

Итак, в этом, на мой взгляд, заключается потенциал использования IL напрямую. Генерировать методы с его помощью, по-моему, занятие бесполезное, для этого есть более подходящие средства. Но вот модификация существующего кода IL может помочь в определенных ситуациях.

Expression'ы оказывается компилятся в нечто не самое быстрое, как всем может показаться. Цитата из статьи от Sergey Teplyakov:
Expression.Compile creates a DynamicMethod and associates it with an anonymous assembly to run it in a sandboxed environment. This makes it safe for a dynamic method to be emitted and executed by partially trusted code but adds some run-time overhead.

Поэтому, иногда генерация на IL вместо Expression оправдана.
Да, я тоже это заметил. Решил проблему тем, что создавал сборку вручную, что-то вроде:

Код
    public static class RuntimeBase
    {
        static AssemblyBuilder assembly_builder;
        static ModuleBuilder module_builder;
        static int next_id;

        static RuntimeBase()
        {
            AssemblyName an = new AssemblyName("iplibdyn");
            assembly_builder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Save);
            module_builder = assembly_builder.DefineDynamicModule("ImageLibGeneratedCode", "iplibdyn.dll", false);
            next_id = 0;
        }

        public static T CompileExpressionToMethod<T>(LambdaExpression expression, string name)
            where T : class
        {
            if (name == null)
                name = string.Format("CLS{0}", ++next_id);
            else
                name = string.Format("CLS{0}_{1}", ++next_id, name);

            TypeBuilder type_builder = module_builder.DefineType(name);
            MethodBuilder method_builder = type_builder.DefineMethod("DynMethod", MethodAttributes.Public | MethodAttributes.Static);

            try
            {
                expression.CompileToMethod(method_builder);
            }
            catch (Exception E)
            {
                Console.WriteLine("Exception: {0}{1}", E.Message, E.StackTrace);
                Debugger.Break();
            }

            Type type = type_builder.CreateType();

            MethodInfo mi = type.GetMethod("DynMethod");
            return Delegate.CreateDelegate(typeof(T), mi) as T;
        }

        public static TypeBuilder DefineType(string name)
        {
            return module_builder.DefineType(name, TypeAttributes.Public);
        }

        public static void SaveDll()
        {
            assembly_builder.Save("iplibdyn.dll");
        }
    }



В этом случае падения производительности уже не наблюдалось, и смысла генерить IL-код просто не было. Заодно появилась возможность сохранить сборку в файл, посмотреть на IL-код и декомпилировать метод.
Нет, не наносекунды. У меня подобные методы — автосгенерированный код для обработки изображений, одна функция может работать несколько миллисекунд. Так вот разница была не наносекунды, а разы.
Упомянутая проверка перед вызовом из anonymous assembly — добавляет наносекунды. Benchmark приведен. Если проблема в том что у вас очень много вызовов (например в цикле и наносекунды накапливаются в миллисекунды) я бы решал проблему перенеся и цикл в anonymous assembly.
Видимо, оверхед присутствует при пересечении границы в обе стороны. Да, у меня миллионы вызовов обычных методов из автосгенерённой функции, которые прекрасно инлайнятся, если не использовать простой вариант с Expression.Compile.
UFO just landed and posted this here
в С# в 2018 году изобрели метапрограмирование по данным от рефлексии?
Нет, в 2018 году про это написали пост на хабре.
Слайды с кодом, особенно автосгенерированным, очень плохо читаются. И этот код нельзя скопировать.
Очень странно, что все это работает, потому что когда я спрашивал у команды как это сделать, мне сказали «низя by design». Подробности в этом issue.
Sign up to leave a comment.