.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
实现方式:
最普通的用循环的实现方式:(不涉及动态编程的内容,参考作用)
对参数进行循环。
{
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的情况)
// 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.通过接口来调用结果;
// 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.通过接口来调用结果;
// 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 |
由此可见:
后面三种动态方式的性能差不多,虽然元编程不花费时间在编译上,但其技术难度相对也高些。
相关例子下载