.NET动态方法。最棒的表演最棒、方法、动态、NET

2023-09-03 22:08:09 作者:少女眼中梦魇

什么是一个动态创建动态的方法,但最好的方法是一样的有效的,如果它是在VS编译?

What is the best way to create dynamic method on a fly but have it same efficient if it was compiled in VS?

说,我想创建一个计算器。用户输入的公式说A + B / C * 0.5;

Say I want create a calculator. User inputs formula say A + B / C * 0.5;

我要的是能够创造像Func键将接受A,B,C为双参数和返回值的两倍。

What I want is to be able to create something like Func which will accept A,B,C as double parameters and return double.

参数类型和返回类型总是一倍。参数的数量是可变的,但至少有一个。

The parameters type and the return type are always double. Number of parameters are variable but at least one.

这些公式可以更改/加频繁。一旦一个公式编译这将是低延迟code可以称之为1000次/秒的部分。

These formulas can be changed / added often. Once a formula 'compiled' it will be part of low latency code which can be called 1000 times / sec.

我需要找到构建简单和可靠的方法,但它的必须具有静态建立和优化方法的准确性能品质

I need to find simple and reliable way to build it but it must have exact performance qualities of statically built and optimised method.

推荐答案

在此我发现微软博客(生成动态方法)和静态方法之间的性能比较,编译EX pression树和白细胞介素注射。

I have found Microsoft blog on this (Generating Dynamic Methods ) and compared performance between static method, compiled expression tree and IL injection.

下面是code:

    static void Main(string[] args)
    {
        double acc = 0;

        var il = ILFact();
        il.Invoke(1);

        var et = ETFact();
        et(1);

        Stopwatch sw = new Stopwatch();

        for (int k = 0; k < 10; k++)
        {
            long time1, time2;

            sw.Restart();

            for (int i = 0; i < 30000; i++)
            {
                var result = CSharpFact(i);
                acc += result;
            }

            sw.Stop();

            time1 = sw.ElapsedMilliseconds;

            sw.Restart();

            for (int i = 0; i < 30000; i++)
            {
                double result = il.Invoke(i);
                acc += result;
            }

            sw.Stop();

            time2 = sw.ElapsedMilliseconds;

            sw.Restart();

            for (int i = 0; i < 30000; i++)
            {
                var result = et(i);
                acc += result;
            }

            sw.Stop();

            Console.WriteLine("{0,6} {1,6} {2,6}", time1, time2, sw.ElapsedMilliseconds);
        }

        Console.WriteLine("\n{0}...\n", acc);
        Console.ReadLine();
    }

    static Func<int, int> ILFact()
    {
        var method = new DynamicMethod(
        "factorial", typeof(int),
        new[] { typeof(int) }
        );

        var il = method.GetILGenerator();
        var result = il.DeclareLocal(typeof(int));
        var startWhile = il.DefineLabel();
        var returnResult = il.DefineLabel();

        // result = 1

        il.Emit(OpCodes.Ldc_I4_1);
        il.Emit(OpCodes.Stloc, result);

        // if (value <= 1) branch end

        il.MarkLabel(startWhile);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldc_I4_1);
        il.Emit(OpCodes.Ble_S, returnResult);

        // result *= (value--)

        il.Emit(OpCodes.Ldloc, result);
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Dup);
        il.Emit(OpCodes.Ldc_I4_1);
        il.Emit(OpCodes.Sub);
        il.Emit(OpCodes.Starg_S, 0);
        il.Emit(OpCodes.Mul);
        il.Emit(OpCodes.Stloc, result);

        // end while

        il.Emit(OpCodes.Br_S, startWhile);

        // return result

        il.MarkLabel(returnResult);
        il.Emit(OpCodes.Ldloc, result);
        il.Emit(OpCodes.Ret);

        return (Func<int, int>)method.CreateDelegate(typeof(Func<int, int>));
    }

    static Func<int, int> ETFact()
    {
        // Creating a parameter expression.
        ParameterExpression value = Expression.Parameter(typeof(int), "value");

        // Creating an expression to hold a local variable. 
        ParameterExpression result = Expression.Parameter(typeof(int), "result");

        // Creating a label to jump to from a loop.
        LabelTarget label = Expression.Label(typeof(int));

        // Creating a method body.
        BlockExpression block = Expression.Block(

        // Adding a local variable.
        new[] { result },

        // Assigning a constant to a local variable: result = 1
        Expression.Assign(result, Expression.Constant(1)),

        // Adding a loop.
        Expression.Loop(

        // Adding a conditional block into the loop.
        Expression.IfThenElse(

        // Condition: value > 1
        Expression.GreaterThan(value, Expression.Constant(1)),

        // If true: result *= value --
        Expression.MultiplyAssign(result,
        Expression.PostDecrementAssign(value)),

        // If false, exit from loop and go to a label.
        Expression.Break(label, result)
        ),

        // Label to jump to.
        label
        )
        );

        // Compile an expression tree and return a delegate.
        return Expression.Lambda<Func<int, int>>(block, value).Compile();
    }

    static int CSharpFact(int value)
    {
        int result = 1;
        while (value > 1)
        {
            result *= value--;
        }

        return result;
    }

下面有3个运行作出i7-920。建设 - 发行64

Here are 3 runs made on i7-920. Build - Release x64

583    542    660
577    578    666
550    558    652
576    575    648
570    574    641
560    554    640
558    551    650
561    551    666
624    638    683
564    581    647

-3778851060...

482    482    557
489    490    580
514    517    606
541    537    626
551    524    641
563    555    631
552    558    644
572    541    652
591    549    652
562    552    639

-3778851060...

482    482    560
507    503    591
525    543    596
555    531    609
553    556    634
540    552    640
579    598    635
607    554    639
588    585    679
547    560    643

-3778851060...

平均值:554 549 634

Averages: 554 549 634

静态VS IL - (!)白细胞介素1%的速度不知道为什么,虽然

Static vs IL - IL 1% faster (!) no idea why though

静态VS ET - 比EX pression树要快静态14%

Static vs ET - static 14% faster than expression tree

修改(2014年2月):我只是跑了code以上(具有非常轻微的修改)在.NET 4.5和更快的CPU,并得到了新的套的成绩:方法/ ET - 9%,法/ IL - 4%

EDIT (Feb 2014) : I just ran the code above (with very slight modifications) on .NET 4.5 and faster CPU and got the new sets of results: Method / ET - 9%, Method / IL - 4%

因此​​,previous结果不再有效 - 的静态方法调用总是快 ..

Hence the previous results are not valid any more - the static method call is always faster..

*不知道它是否是新的硬件( i7-3820 的)或新的.NET或者我做错了什么,在旧的测试。*

*Not sure whether it is new hardware (i7-3820) or new .NET or perhaps I did something wrong in the old test.*

另一个有趣的结果是,在 32位非常相同的code中的3间表演的绝对没有什么区别 的

Another interesting result is that in 32-bit the very same code shows absolutely NO difference between the 3.

Method IL     ET    
--------------------
368    382    399
367    382    399
367    382    399
367    382    400
367    383    400
367    382    399
367    383    399
367    382    399
367    382    399
367    383    400
367    382    399
367    382    399
367    382    399
367    382    399
367    383    400
367    382    400
367    383    399
367    383    400
367    382    399
367    382    400

-7557702120...

--------------------
367.05 382.30 399.35