URL生成方式性能优化结果
2009-11-19 10:33 by 老赵, 17779 visits继上次发现URL生成的性能问题之后,我最近一直在关注一些细节的性能优化。这些优化方式不是宏观的,理论的,而是在实践上对相同问题的不同做法进行探索。我把探索的过程和结论都发布在博客上了,从结果上看性能提高是比较明显的。但是,把它们用于解决实际问题时,效果又会如何呢?我把MvcPatch进行了一些修改,然后再使用UrlGenBenchmark进行了一番比较。
对于性能方面的优化,我做了以下一些改进。首先,我使用Fluent Interface来代替原有的Lambda表达式构建方式,期望可以减小表达式树构建的开销。此时,原本使用Lambda表达式的做法:
public static string ToPost(this UrlHelper helper, Blog blog, Post post) { return helper.Action<BlogController>(c => c.Post(blog, post)); }
现在就变成了:
public static string ToPostByFluent(this UrlHelper helper, Blog blog, Post post) { return helper.To<BlogController>().Action(c => c.Post, blog, post); }
此外,对于Attribute标记Binder的做法也作了一些处理,不过没有使用昨天谈到的优化方式,而是通过在Model Binder类型标记RunnableAttribute的做法来复用对象。例如:
同时,如果CustomBinderAttribute也被标记了Runnable,那么这个Attribute也可以被复用。如MvcPatch中的BinderAttribute就被标记为Runnable。更细节的做法可参考MvcPatch及UrlGenBenchmark项目的源代码。
[Reusable] public class PostBinder : IModelBinder, IRouteBinder { ... }
经过测试,我们可以得到如下结果:
将其绘制成图表:
而每秒生成页面的数量则是:
从结果上看,Fluent的做法比Lambda的性能有大约30%的提升,但是和Route,尤其是Raw的做法有明显的差距。在上一篇文章最后我们分析过,使用Lambda构建URL会经历三个部分,它们所占消耗分别是:
- 构造表达式树:约占29%。
- 解析和运算表达式树:约占30%。
- 使用Route生成URL:约占41%。
而使用Fluent Interface时,第一步,即构造对象的阶段,其开销是Lambda表达式的1/3。同时,最后一个步骤的开销不变。因此我们可以估算出第二个步骤的性能提升程度x:
14.37 / 21.67 = 0.29 / 3 + 0.3 * x + 0.41
等号左边两个数字分别是目前Fluent和Lambda两种做法消耗的时间。通过这个方程我们得出x大约是0.51,也就是说在第二步中我们也获得了大约50%的性能提高。客观来说,这个提升还是比较明显的。不过,绝对来看,这个结果并不让我十分满意。不过可能目前还能进行优化的应该是第3个步骤,那么我是否要朝这个方向努力呢?
您可以在这里下载到UrlGenBenchmark V2的代码,然后通过访问以下几个URL来查看各种生成方式的性能:
- /benchmark?iteration=1000&view=ByRaw:使用拼接字符串的方式生成URL
- /benchmark?iteration=1000&view=ByRoute:使用Route生成URL
- /benchmark?iteration=1000:使用Lambda表达式生成URL
- /benchmark?iteration=1000&view=ByFluent:使用Fluent Interface生成URL
占个位,拜读一下!