woodheadz 发表于 10-9-2010 10:22:41

C# 和WPF的性能优化经验分析

我们的产品碰到了比较严重的性能问题,一个600个节点的文件打开时间居然到了夸张的100多秒。我们的产品是功能最强的MindMapping软件,但同时也是最慢的MindMapping软件。 我们因此损失了一些用户。
性能问题直接还影响着我们进一步的产品研发方向,我们计划在产品中加入大量的动画元素,性能问题不解决,就无法实现让人满意的动画效果。
经过仔细分析,我提出以下几个改进方向,认为在完成后,能够把600节点文件的打开时间提升到一到两秒钟左右:
1.不使用RichTextBox渲染文本,而直接调用底层API,对RichText进行尺寸计算,渲染等工作。
2.重构节点的渲染底层,原来大量使用了绑定、template等手段,完全依赖WPF自动完成节点的渲染。平均每个节点的绑定达到上百个,导致节点创建过程很慢,甚至因此被迫加了个节点对象的对象池。重构方案计划完全自行渲染整个节点。
3.重构布局底层,原来的布局底层建立在WPF的基础上,在ArrangeOverride中完成布局,并通过WPF来获得节点的尺寸等信息,导致无法通过多线程来获得速度的提升。几个月前我才进公司不久时,为了解决布局算法导致用户输入很卡的问题,被迫在布局底层加入一个布局请求调度队列。重构方案计划在内存中除了原来已有的数据模型外,再增加一个系统的渲染模型,对渲染模型完成计算后再更新到屏幕。这样可以利用多线程大幅提升速度。
4.重构系统的渲染底层。原来的渲染底层的构建方式过于“WPF”,大量的绑定和XAML导致抽象严重不足,在我对系统进行性能测试的时候发现几乎无法更换单独的部件。而且由于大量自动完成的无法精细控制的关联动作,动画的效率始终无法让人接受。 新的渲染底层借助渲染模型把渲染和布局计算清晰地分割开来,确保动画的效率。同时新的渲染底层对系统内各个部件进行了充分的抽象,确保可以独立更换各个部件以便于测试。

经过几次会议和争论,公司决定让我着手重构。目前渲染底层基本已经建立起来了,被我们头说成“公共厕所”两个超级对象被我拆成了10多个小对象,结构比以前清晰了很多。虽然还有不少渲染没有完成,但600节点单线程1.3秒左右的成绩已经让我们对未来充满信心。

这次重构的总结了几点经验教训:
1.WPF的绑定,template,style组合是UI编程前所未见的超级武器,但在一些性能敏感的场景,千万慎用,它的效率可能比你预想的还要低。
2.之前设计系统渲染底层的同事对WPF理解极深,我第一次看到他在WPF的布局系统的基础上搭建起来的布局引擎时一下惊为天人!也许就是这个原因,他总是倾向于用WPF的思维方式去解决问题,他对WPF的理解却成为他设计过程中的限制,并成为我们这次大规模重构的原因之一。
3.C#语言中有些非常漂亮的语言特性往往可能会成为性能问题的温床。这两天在重构Layout底层的时候我就发现了一个。我们的系统中涉及了大量的多边形计算,之前的Layout引擎使用yield return IEnumerable<>的方式枚举多边形。这种方式好处就是只有枚举被用到的时候才会进行枚举计算,我们系统在枚举多边形的时候会有大量的计算。 但坏处就是当枚举被多次用到时,即便枚举涉及到的所有对象的状态都不变,整个枚举计算还是会被重新执行。我在重构Layout底层的时候仅仅通过用个数组把枚举结果缓存的方式,就获得了超过一倍的性能提升。

最后推荐个.net性能评估工具: ANTS Performance Profiler, 这个工具能把程序执行过程中每个函数甚至到每行代码总共花费的时间和命中次数全部列出,实在是抓出性能问题的绝杀武器 :lol

ubuntuhk 发表于 11-9-2010 14:30:29

回复 #1 woodheadz 的帖子

性能提高近100倍,所有的努力没有白费:good :good :good

woodheadz 发表于 11-9-2010 21:46:20

原帖由 ubuntuhk 于 11-9-2010 14:30 发表 http://www.freeoz.org/ibbs/images/common/back.gif
性能提高近100倍,所有的努力没有白费:good :good :good
嗯,还是比较有成就感的 :lol
我们老板也不错,系统底层重构这么大的动作,说做就让我做了,时间上也不来卡。
我运气也不错,到澳洲的第一份工作就碰到这么个老板 :lol :lol
页: [1]
查看完整版本: C# 和WPF的性能优化经验分析