9

動的C#コードの記述とコンパイルに関するSOに関するいくつかの投稿を読みました。たとえば、この投稿。私はそれがいくつかの方法でできることを理解しています。

ただし、コード呼び出し側の呼び出しは低速です。簡単なベンチマークを実行しましたが、ネイティブメソッドを呼び出すよりも約500倍遅くなります。

私ができるようにしたいのは、DLLをロードし、そのメソッドの1つを直接(「ネイティブに」)呼び出すことと同じです。これにより、必要な速度の利点が得られます。

これを行う最も簡単な方法は何ですか?ダイナミックコードを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());
    }
4

3 に答える 3

8

MethodInfoはい、または非固有のを介して呼び出すDelegateと、確かに遅くなります。秘訣は、そうしないことです。さまざまなアプローチ:

  • 個々のメソッドについては、 などの基本的だが型指定されたデリゲートを経由するActionか、汎用キャッチオールとしてFunc<object[], object>使用し、型指定されたデリゲートDelegate.CreateDelegateを作成するために使用します。

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

    これの別の変形は、ExpressionAPI (.Compile()メソッドを持っている) またはDynamicMethod(を持っているCreateDelegate()) を使用することです。重要なこと:型指定されたデリゲートを取得し、型指定された呼び出し ( ではなく.DynamicInvoke) を使用して呼び出す必要があります。

  • 型全体を生成するより複雑なケースでは、知っているインターフェイスを実装することを検討してください。

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

    また; 最初のキャスト (非常に安価) の後は、静的コードを使用できます。

    foo.Bar();
    

いかなる種類のパフォーマンスの後でも使用しないでください。someDelegate.DynamicInvoke(...)someMethod.Invoke(...)

于 2012-05-16T07:31:18.217 に答える
3

Marc のアドバイスに加えて、「optimize」コンパイラ オプションを指定することで速度を向上させることができます。

var res = foo.CompileAssemblyFromSource(
        new System.CodeDom.Compiler.CompilerParameters()
        {
            GenerateInMemory = true,
            CompilerOptions = "/optimize"
        },
于 2012-05-16T08:07:04.153 に答える
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

于 2012-05-16T08:31:59.680 に答える