提高一下dotnet程序的效率一
异常机制
一直以来,有个想法,就是依靠异常机制传递一些信息。如果软件分了层次,或者模块间传递信息,如果采用返回值,就需要定义这些值的含义。例如1代表什么,2代表什么,好一点的话用枚举类型。后来看到有人采用throw机制来传递用户输入的登录凭据不对什么的,就有了一个错误提示信息在层间传递的想法。故而进行一个测试。
protected void Page_Load(object sender, EventArgs e)
{
DateTime t = DateTime.Now;
try
{
try
{
TestExption();
}
catch(Exception x)
{
throw x;
}
}
catch { }
DateTime t1 = DateTime.Now;
Response.Write((t1 - t).TotalMilliseconds);
}
protected bool TestExption()
{
throw new Exception("ddd");
return false;
}
以上测试的结果是,在我的机器:intel duo T7500, 2GB内存的配置下,最终得到30多毫秒的结果。
如果没有外面一个try…catch…块,那么得到15多毫秒的结果。
如果去掉throw语句,无论有没有try…catch…,有一个还是两个,得到的都是0毫秒。
看来一个try…catch…块的开销是巨大的,特别是嵌套,更吓人。
我对此测试的原因是质疑,质疑的原因是windows程序的结构化异常捕获的SEH链我比较了解。虽然不知道asp.net是如何实现异常捕获机制的,但是看来开销和SEH链一样的巨大。
我认为异常机制不能用作模块间传递信息的方式,只是在你不知道程序什么时候故障的时候才使用这个机制。例如网络错误,在访问数据库的时候发生了故障等等,这些发生几率很小的地方用异常机制。
如果在web上传递信息的时候采用了异常机制,那么并发量大的情况下,这个开销将是吓人的。
创建对象
这里的计时没法用DateTime了,因为我使用的方法里面也没有一个延时的机制,执行时间小于毫秒级。所以借助了网上一位仁兄的高精度计时器,省得自己去写一个。http://dotnet.chinaitlab.com/ASPNET/742827.html
Ok,下面是我的程序
public class MyClass
{
public MyClass()
{
menber1 = "";
menber2 = 0;
m3 = DateTime.Now;
}
protected string menber1;
protected int menber2;
protected DateTime m3;
public int TetsObj()
{
return 0;
}
public static int TestStatic()
{
return 0;
}
}
这是被测试的类,有一个静态方法和实例方法。
protected void Page_Load(object sender, EventArgs e)
{
MyTimer mt = new MyTimer();
double t = mt.GetAbsoluteTime();
MyClass.TestStatic();
double t1 = mt.GetAbsoluteTime();
MyClass mc = new MyClass();
double t2 = mt.GetAbsoluteTime();
mc.TetsObj();
double t3 = mt.GetAbsoluteTime();
Response.Write((t1 - t));
Response.Write("<br />");
Response.Write((t2 - t1));
Response.Write("<br />");
Response.Write((t3 - t2));
Response.Write("<br />");
}
这是测试页面。
下面看输出吧。返回的秒数,也就是多少秒。
7.47301692172186E-05
0.000180958753844607
7.9339692092617E-05
这是第一次启动vs2008调试的时候的数据。
1.18730167741887E-06
6.49524008622393E-06
1.25714177556802E-06
第一次刷新的数据。
1.18730167741887E-06
4.74920670967549E-06
1.18730167741887E-06
再次刷新。
2.09523932426237E-06
8.73015960678458E-06
2.09523750527296E-06
again refresh
2.09523932426237E-06
8.52063567435835E-06
2.16507942241151E-06
again
大概就是这样了。可以看到创建一个对象的时间是调用一个函数的开销的大约4倍以上时间。第一次启动的时候当然这个差距太大了,这是由于object pooling的原因。另外调用静态方法和实例方法的开销一样,如果准确的说,那就是静态方法很多时候更快。
所以,我觉得很多牛人说的没错,创建对象的开销很大,在一个并发很高的web系统里面需要对此进行优化。
测试object pooling
测试的函数,在MyClass类添加两个方法:
public static void Test1()
{
MyClass mc1 = new MyClass();
}
public static void Test2()
{
MyClass mc1 = new MyClass();
MyClass mc2 = new MyClass();
}
public static void Test3()
{
MyClass mc1 = new MyClass();
MyClass mc2 = new MyClass();
MyClass mc3 = new MyClass();
}
页面测试方法:
protected void Page_Load(object sender, EventArgs e)
{
MyTimer mt = new MyTimer();
double t = mt.GetAbsoluteTime();
MyClass.Test1();
double t1 = mt.GetAbsoluteTime();
MyClass.Test1();
double t2 = mt.GetAbsoluteTime();
MyClass.Test1();
double t3 = mt.GetAbsoluteTime();
MyClass.Test1();
double t4 = mt.GetAbsoluteTime();
MyClass.Test2();
double t5 = mt.GetAbsoluteTime();
MyClass.Test2();
double t6 = mt.GetAbsoluteTime();
MyClass.Test3();
double t7 = mt.GetAbsoluteTime();
MyClass.Test3();
double t8 = mt.GetAbsoluteTime();
Response.Write((t1 - t));
Response.Write("<br />");
Response.Write((t2 - t1));
Response.Write("<br />");
Response.Write((t3 - t2));
Response.Write("<br />");
Response.Write((t4 - t3));
Response.Write("<br />");
Response.Write((t5 - t4));
Response.Write("<br />");
Response.Write((t6 - t5));
Response.Write("<br />");
Response.Write((t7 - t6));
Response.Write("<br />");
Response.Write((t8 - t7));
}
输出的结果:
首次启动:
0.000266234954324318
1.18730167741887E-06
1.11745976028033E-06
1.11745976028033E-06
0.000111815887066768
1.39682742883451E-06
0.000126622238894925
1.81587165570818E-06
第一次调用Test1(),花费了巨大的时间。再次连续调用三次,花费的时间是大概差不多的,但是与第一次相比相差几个数量级。
然后调用Test2(),比Test1()多创建了一个对象,时间又回到了和第一次差不多,多了几个数量级。
再次调用Test2(),时间又降低了几个数量级。
调用Test3(),又多了一个对象创建,时间又反弹上去了。最后调用Test3(),时间又降低了下来。
Dotnet的object pooling效果由此可见。所以,当一个应用不断地创建对象,并发量大的时候,内存是提高效率的最重要的手段了。内存不是要够,而是要富裕。如果很多对象被Pooling了,效率自然就会提高。