异常机制一直以来,有个想法,就是依靠异常机制传递一些信息。如果软件分了层次,或者模块间传递信息,如果采用返回值,就需要定义这些值的含义。例如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./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 这是第一次启动vs2008调试的时候的数据。 1.18730167741887E-06 第一次刷新的数据。 1.18730167741887E-06 再次刷新。 2.09523932426237E-06 2.09523932426237E-06 大概就是这样了。可以看到创建一个对象的时间是调用一个函数的开销的大约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了,效率自然就会提高。 |
|