论文部分内容阅读
摘 要:所谓抛出异常就是异常机制通知调用的方法出错了的手段。抛出异常也是很多人诟病异常对性能影响的地方,因为系统性能的开销很多都是由抛出异常引起。但是异常的抛出是无法避免的,手动抛出异常后,进行异常的抛出处理又需要额外的功能。本文主要介绍了抛出异常的产生、对系统性能的影响分析与测试,最后得出相应的结论。
关键词:ASP.net;抛出异常;性能开销;托管代码
中图分类号:TP312 文献标识码: A 文章编号: 2095-8595 (2016) 04-446-04
电子科学技术 URL: http//www.china-est.com.cn DOI: 10.16453/j.issn.2095-8595.2016.04.018
Abstract: The throwing an exception is to the notification call this exception mechanism method error means. Throw exception is also a lot of people criticized the impact of abnormal performance, because it causes the overhead of system performance. But thrown exception can not be avoided, manually throw an exception after exception thrown the treatment they need additional performance overhead. This paper mainly introduces the exception generation, analysis and test of influence on the performance of the system, and draws the relevant conclusion.
Key words: Asp.net; Throw Exception; Performance Everhead; Managed Code引言
在当前的计算机编程语言中,高级程序语言有很多种,而且种类也是良莠不齐,这时候选择一种效率优秀的编程语言就显得至关重要。在常见的这些高级编程语言中,C++和C#是两种优秀的语言,用C++写的程序,有一个很大的优点就是“快”,效率高,而且部署容易;而C#呢,由于它是基于 Framework架构的,必须先要安装运行时库,速度比C++要慢[1,2]。
为了提升.net托管代码的执行效率,本文首先分析了异常的原理,指出异常的产生是不可避免的,并介绍了异常的处理。重点是通过相关程序的模拟测试,将异常抛出问题对系统的效率开销进行了深入的研究,运用多种微软开发的底层测试工具Xpert进行诸多测试。因为异常的抛出是C#的一大特点[3],既然无法规避,那么唯一的办法就是学会正确地使用它,使得异常抛出对系统的影响降到最低。
1 Asp.net框架的效率问题
.net框架是一个多语言组件开发和执行环境,它提供了一个跨语言的统一编程环境。. net框架的目的是方便开发人员更容易地建立Web应用程序和Web服务,使得互联网上的各种应用程序之间,可以使用Web服务进行通信。从层次结构来看,. net框架包括三个主要组成部分:公共语言运行时服务框架和上层的两类应用模板、传统的Windows应用程序模板和基于Asp.net的面向Web的网络应用程序模板。
由于Asp.net框架主要的语言C#是托管代码,因此运行效率要慢于C++。但是公共语言运行库CLR使用即时编译器JIT将公共语言运行库中的各种方法编译成本机x86代码,然后运行本机代码[4]。尽管 JIT编译在编译首次调用的方法比较慢,但所调用的各种方法在运行纯本机代码时都不需要解释性的系统开销。
2 异常抛出对性能的影响
2.1 什么是异常机制
所谓异常机制也就是指的语言平台支持异常这种错误处理模式的机制,比如C#里的Exception对象,try{}catch{}finally{}结构,throw抛出异常的语句等等,均为C#语言里对异常机制的实现。
异常机制是随着语言而存在的,一种语言既然支持异常机制,那么异常就是不可回避的,哪怕程序不设置throw异常,所使用的系统类,也会抛出异常,所以说讨论在系统里用不用异常是非常可笑的事情。异常机制就像系统的后门,当一个过程执行中系统出错的时候,或者系统不正常的时候,就把当时的情况拍个快照生成异常对象,通过特殊的通道通知调用这个过程的方法。时间会稍有延迟,但所调用的各种方法在运行纯本机代码时都不需要解释性的系统开销。
异常对象是异常机制中用来描述异常,记录错误信息,可以说是错误发生是的快照,就跟开车闯红灯被天眼拍到的照片差不多。
2.2 抛出异常
所谓抛出异常就是异常机制中通知调用的方法出错了的手段。而抛出异常也是很多人诟病异常对性能影响的地方,因为系统性能的开销都是由抛出异常所引起的。但是抛出异常也无法避免,因为这是系统本身的特点,你不抛出异常,系统自己也会抛出,一旦系统抛出了异常对象,怎么来避免这个性能的开销?没辙!
比如方法A调用了方法B,方法B访问数据库,出错了,那么B就会抛出异常,而A就需要catch这个异常来处理。
抛出异常有两种形式,一种是系统抛出的异常,一種是认为在处理一段逻辑的时候当逻辑错误,就可以通过抛出一个异常对象,来通知调用这个方法的方法,这里出错了。
2.3 处理异常 所有的异常必须得到妥善的处理,也就是说必须处理所有的异常。因为系统出错就抛异常,所以这个是C#本身的特性,无法避免,故系统里到处都会充斥着try{}catch{}[5]。
但是try{}catch{}本身并不会影响系统的性能,所以在没有异常发生的时候try{}catch{}是不会让你系统变慢的,而一旦发生系统异常,不处理系统就崩溃掉了。
在Asp.net的工具中,一般使用更简单、结构化的try{}catch{}语句来捕获处理各种异常。
try语句简介:
一般处理应用程序在执行期间其代码块发生的异常,需要先把该代码块置于try 块中。
(1)只要try块中的某段代码抛出一个属于catch子句所声明的异常,程序跳过try块中的剩余代码,执行对应catch子句中的异常处理代码。
(2)若try块中没有代码抛出异常,那么程序就会跳过全部catch子句。
(3)若try块中出现一个抛出异常,但不属于catch子句所声明的异常类,则try语句所在的方法将立即退出。
catch语句简介:
当catch子句指定错误类型时,该错误类型一定继承于System.Exception。如果在catch子句中,没指定异常类型的catch子句叫做常规catch子句。
(1)一个try块中,语句只能有一个常规catch子句,如果出现多个catch子句,常规catch子句必须是最后一个catch子句。
(2)catch子句不会捕获已经处理过的异常。
2.4 异常开销对系统性能的影响及分析
如何正确处理异常?这是个很多C#程序员都没最终搞明白的话题,很多人所了解的也无非就是不要用异常处理当业务逻辑,但是何谓用异常来处理逻辑就不得而知了。这里详细说明一下,什么地方要用异常,什么地方不要用。
异常机制是一个由底向上的冒泡过程,所以正常逻辑是,异常由底层抛出,由高层来处理[6]。
要编写可靠性高的应用程序,首先要保证程序可以处理所有的系统异常,也就是可以处理调用.net类库方法的时候,这个方法可能抛出的所有异常。不过不推荐那种大而全的处理方式,不够细致,很多时候会被奇怪的错误搞得追查错误的本源。而且遇到网络问题重试就没法处理。异常必须就近处理,这样才能方便追查。
异常机制是C#的一个特征,因此决定了其不可能逃避,所以讨论异常带来了多大开销都没用,没有必要研究从根本上无法解决的问题。应该弄清楚的是,异常的抛出给系统带来什么样的影响,如何在保证系统正常的基础上减少不必要的性能消耗。
接下来本文用一段程序,模拟测试异常对系统开销的影响。
调用栈深度时间测试代码:
测试结果如表1所示。
其中,平均时间的计算t(x)过程如下,n为试验次数, p(x)为成功概率,s(x)表示不抛出异常所需的平均时的,e(x)表示抛出所需的平均时间,则有:
(1)
由上面的测试结果可以发现随着调用栈的逐渐深入,性能开销也越来越大,所以当出现异常时,应该尽早将之抛出,否则可能会导致系统性能大幅下降。
同时,对输入的数据应该事先在业务逻辑中严格检查。因为try{}catch{}不会造成任何的系统开销,造成系统开销的是throw 抛出异常。
接下来,利用专门测试工具微软官方性能分析工具Xperf对程序出现抛出异常和没有抛出异常的情况分别进行测试模拟。Xperf是微软推出的性能调试工具,可深入调试各种性能问题,运行时对系统的影响相当的小。
经工具软件模拟测试,当有抛出异常时,系统CPU的使用率和程序生存周期的关系图如图1所示,而没有抛出异常时,系统CPU的使用率和程序生存周期的关系图如图2所示。图中上部分是CPU的使用率,两条曲线是表示的是双核CPU,下部分是测试程序的生存周期,上下两部分的横坐标X轴都是时间,单位是秒,纵坐标Y轴是CPU的使用率。
图中显示的是使用Xperf来具体观察哪些模块(Module)在消耗CPU资源。
从上面两张图的对比结果可以明显看出,在抛出异常时,执行时间大约在12秒,而没有抛出异常时执行时间大约只有2秒。
3 结束语
本次测试对比关注的两个模块(module)是ntkrnlpa.exe和mscorwks.dll, 在未异常的情况下,它们俩的开销基本上为0。而在异常的情况下,它们两个开销之和占了整个开销的绝大部分。因此得出了本文所要的结论,异常抛出是不可避免的,异常的抛出是程序健壮性的保证,正确使用异常非常重要,一旦触发了异常则会对系统开销造成影响。但系统不抛出异常时,对性能开销可以忽略不计,因此只要能正确使用异常,不仅可以保证程序的健壮性,也可以保证程序的高效运行。
参考文献
霍夫曼(美)著,李虎等译. C#技术内幕[M].北京:机械工业出版社,2007.
刘志成. Windows程序设计(C#2.0)实例教程[M].北京:电子工业出版社,2011.
唐珺.浅谈C#程序中的异常处理机制[J].计算机安全. 2010(12):63-65.
彭设强,郭孜,鲁徐. ASP.NET MVC中基于AOP的异常处理的实现[J].科技创业月刊.2011(04):173-174.
米婕.论异常与异常处理机制[J].电子制作. 2013(12):76-77.
朱曉龙.异常处理的教学与实践[J].中国科技信息. 2006(13):240-241.
关键词:ASP.net;抛出异常;性能开销;托管代码
中图分类号:TP312 文献标识码: A 文章编号: 2095-8595 (2016) 04-446-04
电子科学技术 URL: http//www.china-est.com.cn DOI: 10.16453/j.issn.2095-8595.2016.04.018
Abstract: The throwing an exception is to the notification call this exception mechanism method error means. Throw exception is also a lot of people criticized the impact of abnormal performance, because it causes the overhead of system performance. But thrown exception can not be avoided, manually throw an exception after exception thrown the treatment they need additional performance overhead. This paper mainly introduces the exception generation, analysis and test of influence on the performance of the system, and draws the relevant conclusion.
Key words: Asp.net; Throw Exception; Performance Everhead; Managed Code引言
在当前的计算机编程语言中,高级程序语言有很多种,而且种类也是良莠不齐,这时候选择一种效率优秀的编程语言就显得至关重要。在常见的这些高级编程语言中,C++和C#是两种优秀的语言,用C++写的程序,有一个很大的优点就是“快”,效率高,而且部署容易;而C#呢,由于它是基于 Framework架构的,必须先要安装运行时库,速度比C++要慢[1,2]。
为了提升.net托管代码的执行效率,本文首先分析了异常的原理,指出异常的产生是不可避免的,并介绍了异常的处理。重点是通过相关程序的模拟测试,将异常抛出问题对系统的效率开销进行了深入的研究,运用多种微软开发的底层测试工具Xpert进行诸多测试。因为异常的抛出是C#的一大特点[3],既然无法规避,那么唯一的办法就是学会正确地使用它,使得异常抛出对系统的影响降到最低。
1 Asp.net框架的效率问题
.net框架是一个多语言组件开发和执行环境,它提供了一个跨语言的统一编程环境。. net框架的目的是方便开发人员更容易地建立Web应用程序和Web服务,使得互联网上的各种应用程序之间,可以使用Web服务进行通信。从层次结构来看,. net框架包括三个主要组成部分:公共语言运行时服务框架和上层的两类应用模板、传统的Windows应用程序模板和基于Asp.net的面向Web的网络应用程序模板。
由于Asp.net框架主要的语言C#是托管代码,因此运行效率要慢于C++。但是公共语言运行库CLR使用即时编译器JIT将公共语言运行库中的各种方法编译成本机x86代码,然后运行本机代码[4]。尽管 JIT编译在编译首次调用的方法比较慢,但所调用的各种方法在运行纯本机代码时都不需要解释性的系统开销。
2 异常抛出对性能的影响
2.1 什么是异常机制
所谓异常机制也就是指的语言平台支持异常这种错误处理模式的机制,比如C#里的Exception对象,try{}catch{}finally{}结构,throw抛出异常的语句等等,均为C#语言里对异常机制的实现。
异常机制是随着语言而存在的,一种语言既然支持异常机制,那么异常就是不可回避的,哪怕程序不设置throw异常,所使用的系统类,也会抛出异常,所以说讨论在系统里用不用异常是非常可笑的事情。异常机制就像系统的后门,当一个过程执行中系统出错的时候,或者系统不正常的时候,就把当时的情况拍个快照生成异常对象,通过特殊的通道通知调用这个过程的方法。时间会稍有延迟,但所调用的各种方法在运行纯本机代码时都不需要解释性的系统开销。
异常对象是异常机制中用来描述异常,记录错误信息,可以说是错误发生是的快照,就跟开车闯红灯被天眼拍到的照片差不多。
2.2 抛出异常
所谓抛出异常就是异常机制中通知调用的方法出错了的手段。而抛出异常也是很多人诟病异常对性能影响的地方,因为系统性能的开销都是由抛出异常所引起的。但是抛出异常也无法避免,因为这是系统本身的特点,你不抛出异常,系统自己也会抛出,一旦系统抛出了异常对象,怎么来避免这个性能的开销?没辙!
比如方法A调用了方法B,方法B访问数据库,出错了,那么B就会抛出异常,而A就需要catch这个异常来处理。
抛出异常有两种形式,一种是系统抛出的异常,一種是认为在处理一段逻辑的时候当逻辑错误,就可以通过抛出一个异常对象,来通知调用这个方法的方法,这里出错了。
2.3 处理异常 所有的异常必须得到妥善的处理,也就是说必须处理所有的异常。因为系统出错就抛异常,所以这个是C#本身的特性,无法避免,故系统里到处都会充斥着try{}catch{}[5]。
但是try{}catch{}本身并不会影响系统的性能,所以在没有异常发生的时候try{}catch{}是不会让你系统变慢的,而一旦发生系统异常,不处理系统就崩溃掉了。
在Asp.net的工具中,一般使用更简单、结构化的try{}catch{}语句来捕获处理各种异常。
try语句简介:
一般处理应用程序在执行期间其代码块发生的异常,需要先把该代码块置于try 块中。
(1)只要try块中的某段代码抛出一个属于catch子句所声明的异常,程序跳过try块中的剩余代码,执行对应catch子句中的异常处理代码。
(2)若try块中没有代码抛出异常,那么程序就会跳过全部catch子句。
(3)若try块中出现一个抛出异常,但不属于catch子句所声明的异常类,则try语句所在的方法将立即退出。
catch语句简介:
当catch子句指定错误类型时,该错误类型一定继承于System.Exception。如果在catch子句中,没指定异常类型的catch子句叫做常规catch子句。
(1)一个try块中,语句只能有一个常规catch子句,如果出现多个catch子句,常规catch子句必须是最后一个catch子句。
(2)catch子句不会捕获已经处理过的异常。
2.4 异常开销对系统性能的影响及分析
如何正确处理异常?这是个很多C#程序员都没最终搞明白的话题,很多人所了解的也无非就是不要用异常处理当业务逻辑,但是何谓用异常来处理逻辑就不得而知了。这里详细说明一下,什么地方要用异常,什么地方不要用。
异常机制是一个由底向上的冒泡过程,所以正常逻辑是,异常由底层抛出,由高层来处理[6]。
要编写可靠性高的应用程序,首先要保证程序可以处理所有的系统异常,也就是可以处理调用.net类库方法的时候,这个方法可能抛出的所有异常。不过不推荐那种大而全的处理方式,不够细致,很多时候会被奇怪的错误搞得追查错误的本源。而且遇到网络问题重试就没法处理。异常必须就近处理,这样才能方便追查。
异常机制是C#的一个特征,因此决定了其不可能逃避,所以讨论异常带来了多大开销都没用,没有必要研究从根本上无法解决的问题。应该弄清楚的是,异常的抛出给系统带来什么样的影响,如何在保证系统正常的基础上减少不必要的性能消耗。
接下来本文用一段程序,模拟测试异常对系统开销的影响。
调用栈深度时间测试代码:
测试结果如表1所示。
其中,平均时间的计算t(x)过程如下,n为试验次数, p(x)为成功概率,s(x)表示不抛出异常所需的平均时的,e(x)表示抛出所需的平均时间,则有:
(1)
由上面的测试结果可以发现随着调用栈的逐渐深入,性能开销也越来越大,所以当出现异常时,应该尽早将之抛出,否则可能会导致系统性能大幅下降。
同时,对输入的数据应该事先在业务逻辑中严格检查。因为try{}catch{}不会造成任何的系统开销,造成系统开销的是throw 抛出异常。
接下来,利用专门测试工具微软官方性能分析工具Xperf对程序出现抛出异常和没有抛出异常的情况分别进行测试模拟。Xperf是微软推出的性能调试工具,可深入调试各种性能问题,运行时对系统的影响相当的小。
经工具软件模拟测试,当有抛出异常时,系统CPU的使用率和程序生存周期的关系图如图1所示,而没有抛出异常时,系统CPU的使用率和程序生存周期的关系图如图2所示。图中上部分是CPU的使用率,两条曲线是表示的是双核CPU,下部分是测试程序的生存周期,上下两部分的横坐标X轴都是时间,单位是秒,纵坐标Y轴是CPU的使用率。
图中显示的是使用Xperf来具体观察哪些模块(Module)在消耗CPU资源。
从上面两张图的对比结果可以明显看出,在抛出异常时,执行时间大约在12秒,而没有抛出异常时执行时间大约只有2秒。
3 结束语
本次测试对比关注的两个模块(module)是ntkrnlpa.exe和mscorwks.dll, 在未异常的情况下,它们俩的开销基本上为0。而在异常的情况下,它们两个开销之和占了整个开销的绝大部分。因此得出了本文所要的结论,异常抛出是不可避免的,异常的抛出是程序健壮性的保证,正确使用异常非常重要,一旦触发了异常则会对系统开销造成影响。但系统不抛出异常时,对性能开销可以忽略不计,因此只要能正确使用异常,不仅可以保证程序的健壮性,也可以保证程序的高效运行。
参考文献
霍夫曼(美)著,李虎等译. C#技术内幕[M].北京:机械工业出版社,2007.
刘志成. Windows程序设计(C#2.0)实例教程[M].北京:电子工业出版社,2011.
唐珺.浅谈C#程序中的异常处理机制[J].计算机安全. 2010(12):63-65.
彭设强,郭孜,鲁徐. ASP.NET MVC中基于AOP的异常处理的实现[J].科技创业月刊.2011(04):173-174.
米婕.论异常与异常处理机制[J].电子制作. 2013(12):76-77.
朱曉龙.异常处理的教学与实践[J].中国科技信息. 2006(13):240-241.