.NET下几种动态生成代码方式比较

场景:

        平时在日常工作中往往会遇到这样的情况:一般的解决方案性能不是最好的,但性能最好的解决方案往往又不一定容易理解。如何兼顾这两者呢?这里有一个Microsoft网站上的例子,它用了几种动态代码生成的方法去实现同一个问题,可以对它们的性能做一个简单的比较,以后到底用那种方法心中就有数了。


要解决的问题:

计算一个多项式的值:
        Y = A0 + A1 * X + A2 * X2 + … + An * Xn



比较指标:

1. 参数只有一个的情况: A0=5.5
2. 参数只有7个的情况: A0=5.5  A1=7.0  A2=15 A3=30 A4=500 A5=100 A6=1
3. 参数有30个的情况:A0=0 A1=1 ... A29=29
4. 参数有50个的情况:A0=0 A1=1 ... A49=49
5. 参数有100个的情况:A0=0 A1=1 ... A99=99

实现方式:

       最普通的用循环的实现方式:(不涉及动态编程的内容,参考作用)
       对参数进行循环。

    public override double Evaluate(double value)
    
{
        
double retval = coefficients[0];

        
double f = value;

        
for (int i = 1; i < coefficients.Length; i++)
        
{
            retval 
+= coefficients[i] * f;
            f 
*= value;
    
        }

        
return(retval);
    }

       显然这样的方式循环是很多的,这里对参数循环,外面还要对指数幂循环。为了减少循环的次数,考虑将上面Evaluate的方法用动态代码的方式实现。


有以下几种动态代码的实现方式:
1. <<PolyCodeSlow.cs>>
步骤:
a.用文件方式动态编写一个类来实现Evaluate方法;
b.动态编译生成DLL;
c.通过反射的方式来调用结果;

动态生成类的文件如下:(参数为7的情况)

// polynomial evaluator
// Evaluating y = 5.5 + 7 X^1 + 7 X^2 + 7 X^3 + 7 X^4 + 7 X^5 + 7 X^6

class Poly_1
{
public double Evaluate(double value)
{
    
return(
        
5.5
        
+ value * (7 
        
+ value * (15 
        
+ value * (30 
        
+ value * (500 
        
+ value * (100 
        
+ value * (1 
    )))))));
}

}


2.<<PolyCode.cs>>
基本思路和上面一样,但稍有不同的是动态编写的类实现了接口。
a.用文件方式动态编写一个类来实现Evaluate方法,并且实现接口;
b.动态编译生成DLL;
c.通过接口来调用结果;

// polynomial evaluator
// Evaluating y = 5.5 + 7 X^1 + 15 X^2 + 30 X^3 + 500 X^4 + 100 X^5 + 1 X^6

class Poly_1001: PolyInterface.IPolynomial
{
public double Evaluate(double value)
{
    
return(
        
5.5
        
+ value * (7 
        
+ value * (15 
        
+ value * (30 
        
+ value * (500 
        
+ value * (100 
        
+ value * (1 
    )))))));
}

}


3.<<PolyCodeDom.cs>>
基本思路和2相同,但动态生成类的方法不同。
a.用CodeDom来动态编写一个类来实现Evaluate方法,并且实现接口;
b.动态编译生成DLL;
c.通过接口来调用结果;

// Polynomial evaluator
// Evaluating Y = 5.5 + 7 X^1 + 15 X^2 + 30 X^3 + 500 X^4 + 100 X^5 + 1 X^6
public class Poly_1001 : PolyInterface.IPolynomial {
    
    
public double Evaluate(System.Double x) {
        
return (5.5 
                    
+ (x 
                    
* (7 
                    
+ (x 
                    
* (15 
                    
+ (x 
                    
* (30 
                    
+ (x 
                    
* (500 
                    
+ (x 
                    
* (100 
                    
+ (x 
                    
* (1 + 0)))))))))))));
    }

}


4.<<PolyEmit.cs>>
这里直接用元编程技术,跳过文本编译生成的过程,直接生成动态编译结果然后调用。
a.建立动态程序集;
b.元编程技术实现动态类和方法;
c.通过动态类的接口调用结果;



结果比较: (数据是在机子上某次运行的结果)

动态代码花费的时间(主要花费在代码编译上)

花费时间(s) 1个参数 7个参数 30个参数 50个参数 100个参数
一般代码方式(PolySimple.cs) 0 0 0 0 0
动态代码方式一(PolyCodeSlow.cs) 0.49 0.37 0.34 0.34 0.36
动态代码方式二(PolyCode.cs) 0.47 0.4 0.36 0.37 0.43
动态代码方式三(PolyCodeDom.cs) 0.51 0.38 0.43 0.39 0.38
动态代码方式四(PolyEmit.cs) 0.01 0 0 0 0

每秒可以运行多少个表达式(性能比较)
  1个参数 7个参数 30个参数 50个参数 100个参数
一般代码方式(PolySimple.cs) 24844149 10976159 3606267 2107176 1050327
动态代码方式一(PolyCodeSlow.cs) 59905 59525 58981 57860 56057
动态代码方式二(PolyCode.cs) 80258857 11703214 2973909 1930444 980942
动态代码方式三(PolyCodeDom.cs) 113636349 11001798 2960087 1917754 769594
动态代码方式四(PolyEmit.cs) 113636349 11916722 2935906 1925118 859074


由此可见:

        后面三种动态方式的性能差不多,虽然元编程不花费时间在编译上,但其技术难度相对也高些。

相关例子下载

posted on 2004-09-10 11:59  风前絮~~  阅读(4506)  评论(3编辑  收藏  举报

导航