代码改变世界

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

2009-02-06 00:12  宝宝合凤凰  阅读(702)  评论(0)    收藏  举报

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

    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耗时少,但测试的结果大大出乎我的意料,如图:

aopconsole

其中用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原生的动态代理实现就可以了。
补充:我让两种实现方式各运行1000次来计算各自的总共耗时,测试的结果是:dotnet:250-350 ;
PostSharp:1250-1300;而且在各自的1000次执行中,大多数次数执行耗时为0毫秒,
而各自的第一次执行耗时与前面测试耗时基本相同,分别为14和79左右,
随后继续以下次数执行,基本都为0毫秒,随后的某次执行会耗时大于0毫秒,
但dotnet实现方式的大于0毫秒的次数比PostSharp出现的次数晚,
DotNet在第80次左右出现一次大于0毫秒的情况,而PostSharp在第25次左右的地方出现一次大于0毫秒的情况。
Tag标签: AOP,性能,PostSharp
posted @ 2008-07-02 17:06 小庄 阅读(2317) 评论(21)  编辑 收藏 网摘 所属分类: Others.Net

  回复  引用    
#1楼 2008-07-02 17:42 | sssssss [未注册用户]
我认为之所以PostSharp的耗时更长的原因就在这里,它把这个方法搞复杂了,所以耗时就长了.
1)性能和代码复杂度没有直接关系

但DotNet原生的动态代理方式并不会这样
2)如果这个是指.Net Remoting,那你应该测试一下其性能再说其优越性.
还有如果你知道.Net Remoting 代理的工作方式估计应该对自己所说的话有所收回:)


  回复  引用    
#2楼 2008-07-02 17:49 | lnt [未注册用户]

为了AOP而AOP.....
  回复  引用  查看    
#3楼 [楼主]2008-07-02 17:54 | 小庄      
@sssssss
呵呵,那些理由只是我猜的,请问为什么PostSharp的耗时更长呢?
  回复  引用  查看    
#4楼 [楼主]2008-07-02 17:55 | 小庄      
@lnt
不明白您说的是啥意思?请详细说明。
  回复  引用    
#5楼 2008-07-02 18:03 | sssssss [未注册用户]
但在我电脑上运行的结果是
dotnet:217
ps:52
  回复  引用  查看    
#6楼 [楼主]2008-07-02 18:05 | 小庄      
@sssssss
哦,忘了说明了,为了模拟真实的运行环境,请生成后Ctrl+F5运行,或者直接运行那个exe文件。
  回复  引用    
#7楼 2008-07-02 18:12 | sssssss [未注册用户]
我本地没有装PS...
但即使这样的测试结果也不客观,毕竟代码多了第一次JIT的时间是比较长的.
所以你应该在jit后多执行几次,取其平均值
  回复  引用    
#8楼 2008-07-02 18:13 | 松下硒鼓 [未注册用户]
滠像aop刷机一体包。*
  回复  引用  查看    
#9楼 [楼主]2008-07-02 18:17 | 小庄      
@sssssss
这个不用装Postsharp吧,直接运行那个exe文件,我的确没有计算平均时间,你的意思是说:因为PostSharp运行时需要的代码比较多,所以jit的时间比较长?
  回复  引用  查看    
#10楼 2008-07-02 18:23 | T.Johnny      
路过。。。。。
  回复  引用  查看    
#11楼 2008-07-02 18:34 | 代码乱了      
易用性方面来说
PostSharp更直观,呵呵,而且可以拦截到任意方法

  回复  引用    
#12楼 2008-07-02 18:54 | 梁利锋 [未注册用户]
真正重要的是:
3、可以拦截任意方法,而 Dynamic Proxy 方式的 AOP 往往采取继承方式来拦截 Virtual 方法。
4、拥有更多的控制权。包括中断执行流程,修改参数和返回值等等。
5、还可以拦截 Field Access、Exception 等操作。
6、无需将对象创建代码改成 "new proxy()",更加透明。

速度方面,就算按你的数据来计算,如果你的主逻辑代码运行需要1秒,则两个运行时间将是1014和1079,就没什么差别了。
  回复  引用  查看    
#13楼 2008-07-02 19:19 | 簡簡單單..      
呵呵
  回复  引用    
#14楼 2008-07-02 20:29 | eee [未注册用户]
@梁利锋
--主逻辑代码运行需要1秒
这种消耗时间的方法不会很多。
  回复  引用  查看    
#15楼 2008-07-03 07:51 | 白菜园      
我的Dotnet:19
PS:68
  回复  引用    
#16楼 2008-07-03 09:37 | joejoe [未注册用户]
我出来打酱油的,看不明白。
  回复  引用  查看    
#17楼 2008-08-19 10:34 | 小No      
这个测试好像不怎么准确,我在你的测试的基础上加多一个Castle的动态代理的测试之后,PostSharp的时间马上减少到11左右,而去掉Castle动态代理的测试代码之后,PostSharp的时间马上又跳回80左右。

不过如果分别单独测试:
DotNet实现是12左右
PostSharp是80左右
Castle的动态代理是110左右

我的测试源代码:
https://files.cnblogs.com/NickYao/AOPConsoleApplication.rar
  回复  引用  查看    
#18楼 [楼主]2008-08-20 10:48 | 小庄      
@小No
呵呵,不管它了,我现在用企业库中的策略注入块来实现AOP,微软做的应该不错的,可要是真的性能太差就骂微软,反正都习惯了。
  回复  引用  查看    
#19楼 2008-08-25 03:59 | 小No      
@小庄

企业库中的策略注入块要继承一个类,这样我觉得不好

  回复  引用  查看    
#20楼 [楼主]2008-08-25 09:44 | 小庄      
@小No
的确是要继承一个类,我参考了artech的文章把它和Unity和WCF结合起来使用非常的方便,但我在使用过程中发现不继承那个类也可以使用!反正我觉得继承不继承的对我的业务实现没什么影响,你的有影响吗?欢迎你阅读我的开发架构设计的文章,多提宝贵意见。
  回复  引用  查看    
#21楼 2008-08-28 11:03 | 小No      
@小庄
因为.NET只能继承一个类,为了实现AOP就占用了这个宝贵位置,我觉得不太适合,起码扩展性受到了一点限制。

虽然目前对你的业务没什么影响,但做架构设计应该考虑远一点,我宁愿使用动态代理或者静态注入