代码改变世界

URL生成方式性能优化结果

2009-11-19 10:33  Jeffrey Zhao  阅读(...)  评论(... 编辑 收藏

继上次发现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会经历三个部分,它们所占消耗分别是:

  1. 构造表达式树:约占29%。
  2. 解析和运算表达式树:约占30%。
  3. 使用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

相关文章