以本机速度运行动态编译的 C# 代码.如何

本文关键字:代码 如何 编译 动态 本机 速度 运行 | 更新日期: 2023-09-27 18:34:13

我已经读过几篇关于编写和编译动态C#代码的文章。例如,这篇文章。我知道可以通过多种方式完成。

但是,调用代码调用程序的速度很慢。我做了一个简单的基准测试,它比调用本机方法慢 500 倍。

我希望能够做的相当于加载 DLL 并直接调用其方法之一("本机"),这将提供我想要的速度优势。

最简单的方法是什么?将动态代码编译为 dll 然后加载它?可以在内存中完成吗?

编辑

我不在乎编译时间。只有执行。

编辑 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());
    }

以本机速度运行动态编译的 C# 代码.如何

是的,如果您通过MethodInfo或非特定Delegate调用,那么它确实会很慢。诀窍是:不要那样做。各种方法:

  • 对于单个方法,请通过基本但类型化的委托(如 Action)或作为通用的 catch-all Func<object[], object> - 并使用 Delegate.CreateDelegate 创建类型化委托:

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

    另一种变体是使用 Expression API(具有 .Compile() 方法)或DynamicMethod(具有 CreateDelegate() )。关键是:你必须得到一个类型化的委托并使用类型化的调用(而不是.DynamicInvoke调用)。

  • 对于生成整个类型的更复杂的情况,请考虑实现您知道的接口,即

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

    同样;在初始转换(非常便宜)之后,你可以只使用静态代码:

    foo.Bar();
    

如果您追求任何类型的表演,请不要使用someDelegate.DynamicInvoke(...)someMethod.Invoke(...)

除了 Marc 的建议之外,您还可以通过指定"优化"编译器选项来提高速度:

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

认为值得展示所有潜在选项的外观及其性能特征。给定以下帮助程序类和函数:

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