WPF官方发布第一个版本至今已经有10年了, 我们几乎在同时也开始了XAML开发。即使经过多年打造,我们依旧尝试提高:我们真的成功打造了高效灵活的控件吗?我没有在其他地方找到任何关于优秀的WPF表格性能比较的介绍,只有少量而年代久远的现在已经不存在的控件的讨论。这个新基准是一种尝试,用来在一些市场相对流行的控件比较中发现我们的优势和不足。
环境
这个基准创建于2016年6月,使用了下面Grid控件。(我们使用的最新试用版)
- System.Windows.Controls 下的DataGrid (PresentationFramework.dll的一部分)
- C1FlexGrid 4.4.0.20162.514 (ComponentOne WPF版)
- C1DataGrid 4.4.0.20162.514 (ComponentOne WPF版)
- XamDataGrid v16.1.16.1.20161.1000 (WPF Controls - Infragistics)
- GridControl v15.2.15.2.10.0 (WPF Controls | DevExpress)
- SfDataGrid 14.1400.0.41 (Syncfusion Essential Studio WPF版 )
- RadGridView 2016.2.503.40 (Telerik WPF Controls | UI WPF版)
- DataGridControl v5.9.5.9.16204.15420 (Xceed DataGrid for WPF)
这个基准运行于ENVY-23 All-in-One Desktop环境,拥有下面的配置。
Intel i7 quad-core CPU @ 3.10 GHz
8 GB RAM
NVIDIA GeForce GT 630M display adapter, Full HD (1920 x 1080) resolution
Windows 10 Pro 64-bit OS
所有的Grid控件设置为相同的尺寸和默认的外观 。
测试应用
我们的基准应用程序允许选择和运行单独的测试或者允许一个接一个运行所有的测试。您可以选择同一个测试运行的次数,这样可以在结果中得到平均值。
我们这样做可以减少其他方面的影响,比如操作系统和其他应用程序的交互。所有展示在这里的结果都会运行10次。下面是应用程序窗口。
基准测试应用程序窗口
如果要分析一个指定的用例,运行一个单独的测试是非常方便的。注意:您需要在不同的测试间比较,在测试运行时请不要改变窗体的尺寸。实际的视图尺寸会影响性能,就像大屏幕会消耗更多时间和虚拟控件等资源用来布局从而影响其他事件。
这个应用会将测试结果写进working文件夹下的Excel文件。如果您需要一个更详细的日志文件,您可以在App.xmal.cs文件中去掉输出到TraceListener的注释。
这里最有趣的地方就是,如何去测试异步UI更新时的复杂操作的运行时间。在几次试验后,我们发现我们想到的就是最合适的方法,用于去获取当Girder UI完成更新时的准确时刻。完整源代码已经附加,您可以去尝试。我们将很高兴收到关于您认为我们可以在某些方面可以提高的反馈。
我们这里不包含任何的控件的二进制版本。如果您想运行应用,您需要下载我们的WPF版本的试用版和其他参与比较的控件的使用版。它们都拥有30天的免费试用期,这将不是问题。
基准
我们选择ListCollectionView作为数据源并且用下面定义的业务对象来填充它。
public class Customer : INotifyPropertyChanged, IEditableObject { public int ID { get; set;} public string Name { get;} public string Country { get;} public int CountryID { get; set;} public bool Active { get; set;} public string First { get; set;} public string Last { get; set;} public DateTime Hired { get; set;} public double Weight { get; set;} public string Father { get;} public string Brother { get;} public string Cousin { get;} }
它提供了12个不同类型的列。
我们尝试为Grid设置相同的条件,对于某些控件,我们更改了某些设置来达到这一要求。
- 自动生成列
- 固定列宽
- 允许在底部新行位置添加新行
- 隐藏分组和搜素面板
- 可编辑的单元格
每一个基准要遵循下面的步骤
1. 移除上一次测试所创建的所有UI。 调用GC.Collect 和GC.WaitForPendingFinalizers方法,使垃圾回收不会影响下次测试;
2. 初始化下次测试和 Stopwatch计时器;
3. 按照需要的次数执行测试;
4. 测量总时间并计算平均结果;
5. 记录结果。
下面解释关于指定基准的实现细节
基准1 创建控件并加载数据。
这个基准创建了一个用户控件,包含一个用于测试的Grid。 将它插入到可视化树中并填充数据。 这样唯一的麻烦就是用代码创建的Xceed DataGridControl并不能正常工作。因此您将看到附加的应用使用XAML创建它。
基准2: 重新加载数据到存在的控件
基准设置DataGrid的ItemsSource为null,用于清空数据和自动生成的列。然后设置ItemsSource 为一个新的ListCollectionView。在代码里,它就像下面这样(对于所有测试的控件都一样)
public override void Load(IList data) { if (data == null) { _grid.ItemsSource = null; } else { _grid.ItemsSource = new ListCollectionView(data); } }
基准3: 排列单列
我们尝试减少测试中自定义的代码,因此大多数情况下我们通过IcollectionView接口来实现排列。
public override void Sort(bool ascending) { ICollectionView dataView = CollectionViewSource.GetDefaultView(_grid.ItemsSource); dataView.SortDescriptions.Clear(); dataView.SortDescriptions.Add(new SortDescription("ID", ascending ?
ListSortDirection.Ascending : ListSortDirection.Descending)); }
这是非常标准的,并且我们希望每个Grid都应内置支持。但是Syncfusion的 SfDataGrid 并没有支持,所以我们需要通过SfDataGrid.SortDescriptions属性来实现排序。
我们测试Infragistics的XamDataGrid时遇到了一个麻烦,他可以使用ICollectionView 排序,但是遇到大数据时变得很慢。当我们想要得到所有控件的对比结果时,我们最终通过FieldLayout.SortedFields来实现它。
基准4和5,滚动100行,滚动整个表单。
我们认为这在测试中可以很好的模拟最终用户交互,但是没有找到一个很好的办法。我们也许可以通过在可视化树中找到滚动条并滚动。但是相关的代码也会影响性能,因此我们决定坚持使特定行进入视图。所有的Grid都拥有ScrollIntoView或者相似的办法来解决这件事。
测试结果
初始化加载时间
为了在所有的测试中避免计算JIT编译和XAML解析的次数,我们从每个控件的一个单独的测试开始整个基准,这跟基准1是几乎是相同的。 当您在整个应用程序生命周期中第一次运行它,您可以发现它跟接下来的运行不同 。 它被测量作为应用程序的启动时间,这对WPF很关键,下面的测试显示了不论数据规格几乎相同的结果。
初始化加载时间
固定宽度基准
下面是1,000,10,000,和100,000项作为数据源的结果
固定列宽下1000 行数据的结果
固定列宽下10,000行数据的结果
固定列宽下100,000行数据的结果
自动列宽基准
在运行了固定列宽的测试之后,我们决定比较一下他们在自动列宽时的表现。我们这项测试只针对四种具有即时自动列宽的Grid。我想在每个Grid的最好情况下得到比较结果,因此我们选择MS的DataGrid 和Telerik的 RadGridView的默认SizeToHeader选项,C1DataGrid的 默认AutoStar选项和Infragistics的XamDataGrid.的默认FieldLength.InitialAuto选项。
为什么我们不对所有的控件都做这项测试呢?我们的C1FlexGrid和DevExpress的GridControl以及Xceed的DataGridControl不支持即时列宽计算。显然,这是设计者为了集中于控件的性能而做出的牺牲。上面的所有控件都支持调用方法来自动计算列宽,但您需要在合适的时间来调用合适的方法。这使它很难衡量整体性能,您也不能确定测试结果是完全依赖于性能的。所以在我们的比较中不包含那些控件。
反之,Syncfusion 的SfDataGrid 控件没有类似MS DataGrid 和其他控件的自动列宽计算的选项,但是由于适用的选项在加载大数据时速度变得很慢,它和其他控件是没法相比的。所以在这个测试中我们也排出了它。
下面是我们分别测试包含1,000, 10,000和100,000条数据的数据源得到的结果。(与固定列宽用的是同样的基准)
自动宽度下1000行数据的结果。
自动宽度下10,000 行数据的结果。
自动宽度下100,000 行数据的结果。
一些条件
我们的结果与很久之前的关于WPF Grid性能的讨论不同,显然现在的控件都具有可视化能力并且可以处理大量数据,如果您打算为您的应用程序选择一个Grid,我们的结果是一个很好的参考。
另外没注意我们没有测试其他的场景,比如,分组、过滤和列数据可视化。还有,性能并不代表全部,您有可能因为一些其它的原因而喜欢上某个控件。比如易用性,XAML定制能力,内嵌支持的特性数量等。
在制定这些基准的时候,我阅读了很多关于控件比较的信息,给我很深的印象就是所有的WPFGrid可以分为下面两类:
- 基于性能的Grid,不包含在自动列宽计算的测试中
- 集中于易用性和XAML运用。
我们的WPF版本中包含以上两种控件,所以您可以选择最适应您需要的一种。更多信息,参考附件中关于C1FlexGrid, C1DataGrid 和MS DataGrid详细功能的比较。
最终,在整个基准的比较的过程中,我情不自禁地要概括我们自己的控件。所以我们又得到一个很有用的信息:2016 v2版本的C1DataGrid控件的速度优于之前的发布版本35%以上。
- 下载WPFGridsBenchmark源代码(19 KB zip)
- 下载1000行数据测试结果(38.5 KB xls)
- 下载10,000行数据测试结果(38.5 KB xls)
- 下载100,000行数据测试结果(38.5 KB xls)
- 下载C1FlexGrid, C1DataGrid 和MS DataGrid 特性比较表(17.7KB xlsx)