WPF(Windows Presentation Foundation)是微软推出的基于Windows的用户界面框架,运行在 .NET Framework 3.0及以上版本。WPF是基于DirectX引擎的,支持GPU硬件加速,在不支持硬件加速时也可以使用软件绘制。尽管WPF有诸多优点,有时我们还是会遇到性能问题,比如界面卡顿,内存泄漏等等。针对WPF程序的性能优化是一个宽泛的问题,本文是对我们这段时间以来所作工作的一个总结。
相同的程序在不同的硬件上运行,会有不同的表现。对渲染能力影响比较大的硬件特性为:
在程序开发过程中制定一些代码规范,可以让我们避免一些性能问题的坑。
在屏幕上绘制图形时,每个元素的布局被调用两次(measure & arrange)。布局过程是一个数学密集型的过程,子元素的数量越多,需要的计算次数就越多。
ItemsControls往往会增加 VisualTree 的深度,如果没有虚拟化会频繁地创建和销毁元素。使用 VirtualizingStackPanel 作为宿主,并设置 VirtualizationMode=Recycling 以便重用元素的容器。
DynamicResources 会创建一个临时表达式并推迟对资源的查找,当真正需要资源值时才去请求,其查找方式和运行时查找相同,这会对性能产生影响。
如果使用 Brush 来填充元素,那么最好设置 Brush 的透明度而不是设置 Element 的透明度。当修改 Element 的透明度时会导致WPF重绘。
<TextBlock>
<Run Text="F"/>
<Run Text="W"/>
TextBlock>
StreamGeometry在处理多个PathGeometry对象时进行了优化,消耗更小的内存,有更高的性能表现。
如果程序需要显示更小的缩略图,那么创建缩小尺寸的图像版本会提高性能。
默认情况下,WPF使用高质量的图像重采集算法,会导致帧率下降和动画断断续续。设置 LowQuality 切换到速度优化算法可以提高性能。
可冻结对象是一种特殊类型的对象,具有两种状态:未冻结和已冻结。比如 Brush,Geometry,冻结对象可以提高性能并减少内存消耗。
绑定错误是WPF程序中最常见的性能问题原因,每次发生绑定错误都会执行一次 perf 命中并尝试解决错误。
把string绑定到 Label.Content时会导致较差的性能。每次数据变动时,旧的string对象会被丢弃并创建一个新的string对象。可以替换为 TextBlock.Text 属性。
当绑定 IEnumerable数据作为 ItemsControl数据源时,WPF会重新包装为 IList 类型。
不要在UI线程上做耗时的动作,UI线程只用来更新界面。
通过设置 Storyboard.DesiredFrameRate 降低动画的帧率。
内存泄漏也会降低程序性能,这块内容比较多,后面会单独篇幅中介绍。
当我们的程序开发完成后仍然存在性能问题,这时就要用到性能分析工具来帮助我们分析应用程序的运行时行为,并确定可以应用的性能优化的类型。比较常用的工具有:
不同的软件,使用方法和侧重点会有区别,可以真实使用并分析总结一下。下面对 dotTrace 做一个简单的介绍。
运行程序,打开dotTrace,我们就可以把进程附加到dotTrace上。运行一段时间,获取运行时情况快照来分析具体耗时情况。
如果是调试程序,还可以联动代码,非常的方便。
找到耗时较长的代码,我们就可以针对性的对某个方法进行优化。比如针对一个通过反射动态获取字段的代码块,优化前后的对比如下:
比如一个 string.Split() 方法的优化:
string.Split() 方法内部会自动转换成 char[] 类型,如果能直接传入 char[] 类型会减少方法的耗时。
像上面对方法的优化还有很多,我们最好对照 dotTrace 报表为每一个耗时方法都去分析为什么耗时?还可以怎么优化?
在上面的分析做完之后,我们发现有个窗口还是占用比较高的耗时和CPU。这是一个第三方的图表控件 DevExpress ,用来刷新波动率图像。在尝试优化使用方法后,还是决定自己封装插件进行图像的绘制。我们把图像上所用到的元素分类抽象出来:
DevExpress拥有.NET开发需要的平台控件,包含600多个UI控件、报表平台等一系列辅助工具,可为桌面、Web和移动应用提供直观的解决方案。
dotMemory 是一个 .NET 的内存分析工具,可以帮助你优化 .NET 应用的内存使用,找出内存泄漏和其他的内存使用问题.
内存泄漏是一个内容比较多的主题,我们会有另外一篇文章具体来总结这方面的工作。最简单使用场景:
程序性能的优化是一个长周期的工作,我们也会持续的进行。本次比较笼统的总结了我们这段时间以来的工作,希望对你有所帮助。