实施例: 我有一个列表和一个包含10000个相同元素的树。
在进行基准测试时,我在排序时间方面有很大差异: List.Sort()(约60毫秒) TreeView.Sort()(约3600毫秒)
我特意将比较器编码为完全相同。
根据下面的代码,有人可以解释时间上的差异吗?
height
答案 0 :(得分:3)
但是你的比较器并不完全相同。 comp_list
是IComparer<string>
。它的Compare
方法接受string
个参数,而ToString
方法只返回字符串。很可能JIT编译器可以用只返回原始引用的代码替换对string.ToString
的所有调用。
实际上,将您的comp_list.Compare
方法替换为:
public int Compare(string xx, string yy)
{
return xx.CompareTo(yy);
}
导致运行时间没有差异。
comp_tree
是IComparer
。传递给它的项目是TreeNode
引用。比较器必须在该对象上调用ToString
方法,然后必须返回Text
属性的值。
简单地说,string.ToString
比TreeNode.ToString
快得多。
实际上,还有更多的事情要发生。我稍微修改了你的代码以便比较并计算它们。事实证明,比较确实需要更长的时间,正如我在上面所推测的那样,但TreeView
排序的比较大约是List
排序的两倍。我怀疑它也花了很多时间交换东西。这是修改过的代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
namespace treeviewSort
{
internal class Program
{
public class comp_list : IComparer<string>
{
public Stopwatch sw = new Stopwatch();
public int CompareCount = 0;
public int Compare(string xx, string yy)
{
++CompareCount;
sw.Start();
string x = xx.ToString();
string y = yy.ToString();
sw.Stop();
return x.CompareTo(y);
}
}
public class comp_tree : IComparer
{
public Stopwatch sw = new Stopwatch();
public int CompareCount = 0;
public int Compare(object xx, object yy)
{
++CompareCount;
sw.Start();
string x = xx.ToString();
string y = yy.ToString();
sw.Stop();
return x.CompareTo(y);
}
}
private static void Main()
{
DoThisTwice();
DoThisTwice();
Console.ReadLine();
}
private static void DoThisTwice()
{
List<string> MyList = new List<string>();
TreeView tv = new TreeView();
int Cnt = 10000;
string s = "";
Random R = new Random();
for (int i = 0; i < Cnt; i++)
{
s = (R.Next(0, Cnt)).ToString();
MyList.Add(s);
tv.Nodes.Add(s);
}
Stopwatch t = new Stopwatch();
t.Start();
comp_list cmp = new comp_list();
MyList.Sort(cmp);
t.Stop();
Console.WriteLine("SORT_LIST={0}. Comparisons={1}. Compare time={2}", t.ElapsedMilliseconds, cmp.CompareCount, cmp.sw.ElapsedMilliseconds);
var tvcmp = new comp_tree();
tv.TreeViewNodeSorter = tvcmp;
Stopwatch tt = new Stopwatch();
tt.Start();
tv.Sort();
tt.Stop();
Console.WriteLine("SORT_TREE={0} Comparisons={1}. Compare time={2}", tt.ElapsedMilliseconds, tvcmp.CompareCount, tvcmp.sw.ElapsedMilliseconds);
}
}
}
我运行它两次以消除可能由JIT代码引起的任何延迟。第一次通过确保所有内容都已编译,因此下次没有任何此类延迟。在这个简单的代码中,它似乎没什么区别,但总的来说它是一个好主意。实际上,最好在一次测试中运行100次或更多次,但就我们的目的而言,只运行两次就可以了。
结果很有启发性:
SORT_LIST=20. Comparisons=141289. Compare time=3
SORT_TREE=849 Comparisons=242150. Compare time=25
SORT_LIST=19. Comparisons=141090. Compare time=3
SORT_TREE=850 Comparisons=241987. Compare time=25
查看source code for TreeView.Sort可提供更多见解。基本上发生的是设置Sorted
标志,并调用RefreshNodes。该方法将Nodes
集合中的所有节点复制到一个数组中。然后清除Nodes
集合并调用AddRange
以将节点添加回Nodes
数组。这项工作是在AddInternal方法中完成的。
因此,List.Sort
对项目进行排序,TreeView.Sort
构建一个空的节点列表,然后将节点一次一个地插入到新列表中。这就是为什么需要这么长时间。
答案 1 :(得分:1)
首先,您的comp_tree
课程不应该调用.ToString()
,而应该投放到TreeNode
并使用其Text
属性。
public class comp_tree : IComparer
{
public int Compare(object xx, object yy)
{
string x = ((TreeNode)xx).Text;
string y = ((TreeNode)yy).Text;
return x.CompareTo(y);
}
}
comp_tree.Compare()
的调用频率高于comp_list.Compare()
。
comp_tree :242042
comp_list :144447
所以,我不得不深入研究TreeView.Sort
代码来找到问题。
这个代码肯定不是高性能。它首先清除TreeView中的所有TreeNodes,然后再次按顺序添加排序。这需要时间。
List(T).Sort()
算法使用插入排序,Heapsort或Quicksort。当然高度优化。有关详细信息,请参阅List.Sort的备注部分。
TreeView.Sort
算法使用某种插入排序,但甚至没有稍微优化。当然要慢得多。
答案 2 :(得分:0)
在这个结构中,项目按顺序分配在内存中,排序后必须找到它的位置&#34;另一方面,在内存中,在前一个元素旁边,列表项只引用了下一个元素,但它们不是按顺序排列在内存中。
答案 3 :(得分:0)
在我的电脑上我有这个:
SORT_LIST=84. Comparisons=178337. Compare time=20
SORT_TREE=3283 Comparisons=242048. Compare time=123
SORT_LIST=79. Comparisons=173581. Compare time=19
SORT_TREE=3282 Comparisons=242100. Compare time=121
SORT_LIST=81. Comparisons=174326. Compare time=19
SORT_TREE=3013 Comparisons=242095. Compare time=119
SORT_LIST=76. Comparisons=172590. Compare time=19
SORT_TREE=3075 Comparisons=241952. Compare time=125
SORT_LIST=82. Comparisons=173555. Compare time=19
SORT_TREE=4012 Comparisons=242104. Compare time=121
SORT_LIST=82. Comparisons=171671. Compare time=22
SORT_TREE=3987 Comparisons=242045. Compare time=136
This variant with
public class comp_tree : IComparer
{
public Stopwatch sw = new Stopwatch();
public int CompareCount = 0;
public int Compare(object xx, object yy)
{
++CompareCount;
sw.Start();
string x = ((TreeNode)xx).Text;
string y = ((TreeNode)yy).Text;
sw.Stop();
return x.CompareTo(y);
}
}
SORT_LIST=82. Comparisons=176456. Compare time=19
SORT_TREE=3263 Comparisons=242053. Compare time=36
SORT_LIST=79. Comparisons=181171. Compare time=19
SORT_TREE=3094 Comparisons=241980. Compare time=36
SORT_LIST=84. Comparisons=179307. Compare time=20
SORT_TREE=3064 Comparisons=242165. Compare time=37
SORT_LIST=77. Comparisons=173915. Compare time=19
SORT_TREE=3313 Comparisons=242022. Compare time=36
SORT_LIST=87. Comparisons=186245. Compare time=21
SORT_TREE=3214 Comparisons=241970. Compare time=37
SORT_LIST=78. Comparisons=176906. Compare time=19
SORT_TREE=3349 Comparisons=242058. Compare time=36
如果treeview包含100000 elem,那么.Sort我等不及!!!!