您的位置:首页 > 运维架构

两种AOP实现方式的性能比较

2008-07-02 17:06 567 查看
AOP(Aspect-Oriented Programming,面向方面编程),它是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。我们把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处都基本相似。比如权限认证、日志、异常捕获、事务处理、缓存等。

目前在.Net下实现AOP的方式分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代或修饰原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。动态代理实现方式利用.Net的Attribute和.Net Remoting的代理技术,对对象执行期间的上下文消息进行截取,并以消息传递的方式执行,从而可以在执行期间加入相关处理逻辑实现面向方面的功能(请参考:http://www.cnblogs.com/wayfarer/articles/256909.html);而静态织入的方式实现一般是要依靠一些第三方框架提供特定的语法,例如PostSharp,它的实现方式是采用 MSIL Injection和MSBuild Task在编译时置入方面的代码,从而实现AOP。

PostSharp和基于 Dynamic Proxy 方式做个比较。

1、由于采用 MSIL Injection,因此静态代码注入的执行效率要高于使用 Reflection Emit。

2、使用 MSBuild Task,使得开发人员可以像使用编译器内置 Attribute 那样使用 AOP。

3、可以拦截任意方法,而 Dynamic Proxy 方式的 AOP 往往采取继承方式来拦截 Virtual 方法。

4、拥有更多的控制权。包括中断执行流程,修改参数和返回值等等。

5、还可以拦截 Field Access、Exception 等操作。

6、无需将对象创建代码改成 "new proxy()",更加透明。

7、可以使用通配符进行多重拦截匹配。

8、静态注入带来的问题更多的是注入代码的质量和调试复杂度。
看了以上比较,让我对PostSharp佩服的五体投地,决定研究一下,既然执行效率要要比动态代理的方式高,我就想知道到底高多少?好让我忏悔以前使用动态代理方式的错误,并一心转到PostSharp上来。
为此我写了一个Console程序,用StopWatch来查看两种AOP实现方式的耗时是多少,相差多少?这个Console程序在此下载(vs2008),本来我以为肯定是PostSharp耗时少,但测试的结果大大出乎我的意料,如图:



其中用dotNet原生的动态代理方式耗时为14毫秒,而Postsharp实现方式的的耗时为79毫秒,最下面那个0毫秒的是无AOP时该方法的执行时间。如果把PostSharp实现放在动态代理实现的前面执行,PostSharp的耗时更多,而DotNet动态代理的方式耗时更少,我想这可能是因为dotnet运行环境对方法的运行参数进行了缓存所致,第二次运行比第一次运行更快一点。
用Reflector查看Postsharp实现方式方法的源代码:
public int MyTestMethod(int p1, int p2, out int p3)
{
// This item is obfuscated and can not be translated.
int ~returnValue~4;
object[] ~arguments~6;
MethodExecutionEventArgs ~laosEventArgs~7;
try
{
object[] objArray1 = new object[3];
objArray1[0] = p1;
objArray1[1] = p2;
~arguments~6 = objArray1;
~laosEventArgs~7 = new MethodExecutionEventArgs(methodof(PostSharpTestClass.MyTestMethod, PostSharpTestClass), this, ~arguments~6);
~PostSharp~Laos~Implementation.PostSharpPerformanceTestAttribute~1.OnEntry(~laosEventArgs~7);
if (~laosEventArgs~7.get_FlowBehavior() == 3)
{
~returnValue~4 = (int) ~laosEventArgs~7.get_ReturnValue();
~arguments~6[2] = (int) p3;
return ~returnValue~4;
}
int tempint = 0;
for (int i = 1; i <= 100; i++)
{
tempint += i;
}
p3 = tempint;
int CS$1$0000 = p1 + p2;
~returnValue~4 = CS$1$0000;
~arguments~6[2] = (int) p3;
~laosEventArgs~7.set_ReturnValue(~returnValue~4);
~PostSharp~Laos~Implementation.PostSharpPerformanceTestAttribute~1.OnSuccess(~laosEventArgs~7);
p3 = (int) ~arguments~6[2];
~returnValue~4 = (int) ~laosEventArgs~7.get_ReturnValue();
}
catch (Exception ~exception~5)
{
~laosEventArgs~7.set_Exception(~exception~5);
~arguments~6[2] = (int) p3;
~PostSharp~Laos~Implementation.PostSharpPerformanceTestAttribute~1.OnException(~laosEventArgs~7);
p3 = (int) ~arguments~6[2];
switch (~laosEventArgs~7.get_FlowBehavior())
{
case 1:
return ~returnValue~4;
case 3:
return (int) ~laosEventArgs~7.get_ReturnValue();
}
throw;
}
finally
{
~arguments~6[2] = (int) p3;
~laosEventArgs~7.set_ReturnValue(~returnValue~4);
~PostSharp~Laos~Implementation.PostSharpPerformanceTestAttribute~1.OnExit(~laosEventArgs~7);
p3 = (int) ~arguments~6[2];
~returnValue~4 = (int) ~laosEventArgs~7.get_ReturnValue();
}
return ~returnValue~4;
}

给我的感觉是:好长,好繁。我认为之所以PostSharp的耗时更长的原因就在这里,它把这个方法搞复杂了,

所以耗时就长了,但DotNet原生的动态代理方式并不会这样,但对性能也有不小的影响。

综上所述:我认为我们在使用AOP技术进行开发时,除非很有必要用到PostSharp提供的那么多功能,

要是只是实现权限验证,日志、异常捕获等,用DotNet原生的动态代理实现就可以了。

[code]补充:我让两种实现方式各运行1000次来计算各自的总共耗时,测试的结果是:dotnet:250-350 ;

PostSharp:1250-1300;而且在各自的1000次执行中,大多数次数执行耗时为0毫秒,

而各自的第一次执行耗时与前面测试耗时基本相同,分别为14和79左右,

随后继续以下次数执行,基本都为0毫秒,随后的某次执行会耗时大于0毫秒,

但dotnet实现方式的大于0毫秒的次数比PostSharp出现的次数晚,

DotNet在第80次左右出现一次大于0毫秒的情况,而PostSharp在第25次左右的地方出现一次大于0毫秒的情况。

[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: