Вопрос по c# – Запускать динамически скомпилированный код C # на собственной скорости ... как?

9

Я прочитал несколько постов на SO о написании и компиляции динамического кода C #. Например,эта почта, Я так понимаю, это можно сделать несколькими способами.

Тем не менее, вызов кода вызывающего происходит медленно. Я сделал простой тест, и он примерно в 500 раз медленнее, чем вызов нативного метода.

То, что я хочу сделать, - это эквивалент загрузки библиотеки DLL и непосредственного вызова одного из ее методов (& quot; "родной"), что даст желаемые преимущества в скорости.

Какой самый простой способ сделать это? Скомпилировать динамический код в DLL, а затем загрузить его? Это может быть сделано в памяти?

EDIT

Я не забочусь о времени компиляции. Только исполнение.

EDIT 2, 3

Вот эталонный код, который я написал:

    public static int Execute(int i) { return i * 2; }

    private void button30_Click(object sender, EventArgs e)
    {
        CSharpCodeProvider foo = new CSharpCodeProvider();

        var res = foo.CompileAssemblyFromSource(
            new System.CodeDom.Compiler.CompilerParameters()
            {
                GenerateInMemory = true,
                CompilerOptions = @"/optimize",                    
            },
            @"public class FooClass { public static int Execute(int i) { return i * 2; }}"
        );

        var type = res.CompiledAssembly.GetType("FooClass");
        var obj = Activator.CreateInstance(type);
        var method = type.GetMethod("Execute");
        int i = 0, t1 = Environment.TickCount, t2;
        //var input = new object[] { 2 };

        //for (int j = 0; j < 10000000; j++)
        //{
        //    input[0] = j;
        //    var output = method.Invoke(obj, input);
        //    i = (int)output;
        //}

        //t2 = Environment.TickCount;

        //MessageBox.Show((t2 - t1).ToString() + Environment.NewLine + i.ToString());

        t1 = Environment.TickCount;

        for (int j = 0; j < 100000000; j++)
        {
            i = Execute(j);
        }

        t2 = Environment.TickCount;

        MessageBox.Show("Native: " + (t2 - t1).ToString() + Environment.NewLine + i.ToString());

        var func = (Func<int, int>) Delegate.CreateDelegate(typeof (Func<int, int>), method);

        t1 = Environment.TickCount;

        for (int j = 0; j < 100000000; j++)
        {
            i = func(j);
        }

        t2 = Environment.TickCount;

        MessageBox.Show("Dynamic delegate: " + (t2 - t1).ToString() + Environment.NewLine + i.ToString());

        Func<int, int> funcL = Execute;

        t1 = Environment.TickCount;

        for (int j = 0; j < 100000000; j++)
        {
            i = funcL(j);
        }

        t2 = Environment.TickCount;

        MessageBox.Show("Delegate: " + (t2 - t1).ToString() + Environment.NewLine + i.ToString());
    }
Вы учли время компиляции? как выглядит ваш тест? Botz3000
выполнение среды сборки C # не в 500 раз медленнее, чем выполнение сборки C # ранее. Однако они идентичны, но необходимо учитывать накладные расходы. Каков ваш тест? Используете ли вы Reflection.Emit или какой-либо сервис компилятора для компиляции? Rune FS
В заключение отметим, что приведенный выше тест, когда он точнее рассчитан, на самом деле на 1100 раз медленнее. Решение для делегата, которое дал Марк, в 8,4 раза медленнее, чем нативное. IamIC
Invoke - это то, что медленно, что является центром моего вопроса: как вы можете вызвать метод на собственной скорости? IamIC

Ваш Ответ

3   ответа
3

Помимо совета Марка, вы могли бы улучшить скорость, указав параметр "оптимизировать". опция компилятора:

var res = foo.CompileAssemblyFromSource(
        new System.CodeDom.Compiler.CompilerParameters()
        {
            GenerateInMemory = true,
            CompilerOptions = "/optimize"
        },
1

Думал, что стоит показать, как выглядели все потенциальные варианты и их характеристики производительности. Даны следующие вспомогательные классы и функции:

public void Test(Func<int> func)
{        
    var watch = new Stopwatch();
    watch.Start();
    for (var i = 0; i <= 1000000; i++)
    {
        var test = func();
    }
    Console.WriteLine(watch.ElapsedMilliseconds);
}

public class FooClass { public int Execute() { return 1;}}

Настройка и исполнение:

using (Microsoft.CSharp.CSharpCodeProvider foo = 
       new Microsoft.CSharp.CSharpCodeProvider())
{
    var res = foo.CompileAssemblyFromSource(
        new System.CodeDom.Compiler.CompilerParameters() 
        {  
            GenerateInMemory = true 
        }, 
        "public class FooClass { public int Execute() { return 1;}}"
    );

    var real = new FooClass();
    Test(() => real.Execute());                   // benchmark, direct call

    var type = res.CompiledAssembly.GetType("FooClass");
    var obj = Activator.CreateInstance(type);    
    var method = type.GetMethod("Execute");
    var input = new object[] { };                
    Test(() => (int)method.Invoke(obj, input));   // reflection invoke  

    dynamic dyn = Activator.CreateInstance(type);  
    Test(() => dyn.Execute());                    // dynamic object invoke

    var action = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), null, method); 
    Test(() => action());                         // delegate
}

Результаты:

8     // direct
771   // reflection invoke
41    // dynamic object invoke
7     // delegate

Поэтому в тех случаях, когда вы не можете использовать делегатов (если вы недостаточно знаете?), Вы можете попробоватьdynamic.

Error: User Rate Limit Exceeded IamIC
8

Да, если вы вызываете черезMethodInfo или неспецифическийDelegateтогда это действительно будет медленно. Хитрость заключается в следующем:don't do that, Различные подходы:

  • for individual methods, go via a basic but typed delegate, such as Action, or as a generic catch-all, Func<object[], object> - and use Delegate.CreateDelegate to create a typed delegate:

    Action doSomething = (Action)Delegate.CreateDelegate(typeof(Action), method);
    

    another variant of this is to use the Expression API (which has a .Compile() method), or DynamicMethod (which has CreateDelegate()). The key thing: you must get a typed delegate and invoke using typed invoke (not .DynamicInvoke).

  • for more complex cases where you are generating whole types, consider implementing an interface you know about, i.e.

    IFoo foo = (IFoo)Activator.CreateInstance(...);
    

    again; after the initial cast (which is very cheap) you can just use static code:

    foo.Bar();
    

Делатьnot использованиеsomeDelegate.DynamicInvoke(...) или жеsomeMethod.Invoke(...) если вы после любого вида производительности.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededvar func = (Func<int, int>) Delegate.CreateDelegate(typeof (Func<int, int>), method); ... i = func(j);
Error: User Rate Limit Exceededreturn i*2;Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded

Похожие вопросы