.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)
    public override double Evaluate(double value) {
    { double retval = coefficients[0];
        double retval = coefficients[0];
 double f = value;
        double f = value;
 for (int i = 1; i < coefficients.Length; i++)
        for (int i = 1; i < coefficients.Length; i++) {
        { retval += coefficients[i] * f;
            retval += coefficients[i] * f; f *= value;
            f *= value; 
     }
        } return(retval);
        return(retval); }
    }
       显然这样的方式循环是很多的,这里对参数循环,外面还要对指数幂循环。为了减少循环的次数,考虑将上面Evaluate的方法用动态代码的方式实现。
有以下几种动态代码的实现方式:
1. <<PolyCodeSlow.cs>>
步骤:
a.用文件方式动态编写一个类来实现Evaluate方法;
b.动态编译生成DLL;
c.通过反射的方式来调用结果;
动态生成类的文件如下:(参数为7的情况)
 // polynomial evaluator
// polynomial evaluator // Evaluating y = 5.5 + 7 X^1 + 7 X^2 + 7 X^3 + 7 X^4 + 7 X^5 + 7 X^6
// 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
class Poly_1 {
{ public double Evaluate(double value)
public double Evaluate(double value) {
{ return(
    return( 5.5
        5.5 + value * (7
        + value * (7  + value * (15
        + value * (15  + value * (30
        + value * (30  + value * (500
        + value * (500  + value * (100
        + value * (100  + value * (1
        + value * (1  )))))));
    ))))))); }
} }
}
2.<<PolyCode.cs>>
基本思路和上面一样,但稍有不同的是动态编写的类实现了接口。
a.用文件方式动态编写一个类来实现Evaluate方法,并且实现接口;
b.动态编译生成DLL;
c.通过接口来调用结果;
 // polynomial evaluator
// polynomial evaluator // Evaluating y = 5.5 + 7 X^1 + 15 X^2 + 30 X^3 + 500 X^4 + 100 X^5 + 1 X^6
// 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
class Poly_1001: PolyInterface.IPolynomial {
{ public double Evaluate(double value)
public double Evaluate(double value) {
{ return(
    return( 5.5
        5.5 + value * (7
        + value * (7  + value * (15
        + value * (15  + value * (30
        + value * (30  + value * (500
        + value * (500  + value * (100
        + value * (100  + value * (1
        + value * (1  )))))));
    ))))))); }
} }
}
3.<<PolyCodeDom.cs>>
基本思路和2相同,但动态生成类的方法不同。
a.用CodeDom来动态编写一个类来实现Evaluate方法,并且实现接口;
b.动态编译生成DLL;
c.通过接口来调用结果;
 // Polynomial evaluator
// Polynomial evaluator // Evaluating Y = 5.5 + 7 X^1 + 15 X^2 + 30 X^3 + 500 X^4 + 100 X^5 + 1 X^6
// 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 class Poly_1001 : PolyInterface.IPolynomial { 
     public double Evaluate(System.Double x) {
    public double Evaluate(System.Double x) { return (5.5
        return (5.5  + (x
                    + (x  * (7
                    * (7  + (x
                    + (x  * (15
                    * (15  + (x
                    + (x  * (30
                    * (30  + (x
                    + (x  * (500
                    * (500  + (x
                    + (x  * (100
                    * (100  + (x
                    + (x  * (1 + 0)))))))))))));
                    * (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 | 
由此可见:
后面三种动态方式的性能差不多,虽然元编程不花费时间在编译上,但其技术难度相对也高些。
相关例子下载
 
                    
                     
                    
                 
                    
                 
                
            
         
 
         浙公网安备 33010602011771号
浙公网安备 33010602011771号