令人惊讶的性能差异:List.Constains、SortedList.ContainsKey、DataRowCollection.Contains、DataTable.Select、DataTable.FindBy
最初我想询问查询数据表中特殊行的最快方法。
我测试了 5 种不同方法的性能,结果令人惊讶(对我来说)。
背景: 我在 MS Sql-Server 2005 数据库中创建了一个视图。该视图当前的总行数为 6318 行。因为我必须经常检查给定的 id 是否存在于该视图中,所以我想知道什么是最有效的方法。我在强类型数据集中创建了一个 DataAdapter,它返回所有行并填充数据表。 我的第一个方法是创建一个共享通用列表(Int32),并在应用程序启动时用视图中的 ID 填充它。然后使用 List.Contains 检查是否当前 ID 在此列表中。因为所有行都是不同的,我想知道使用 SortedList 及其 包含Key方法。 然后我还检查了直接访问 Datable 的性能 Select-Method,自动生成(当列定义为主键时)FindBy-method 以及最后但并非最不重要的 DatarowCollection.Contains - 方法。 所以我有 5 种方法来检查我的 ID 是否在该视图(或映射列表/排序列表)中。
我使用 System.Diagnostics.StopWatch 并得到了一些有趣的结果。我认为 SortedList.ContainsKey 一定比 List.Contains 更快,因为它们是不同的且已排序,但事实恰恰相反。 但最令我惊讶的是 DataRowCollection.Contains-Method(我首先忘记的)是迄今为止最快的。它甚至比 dataTable.FindBy 方法快 50 倍。
- 是什么造成了这些差异?
- 我忘记了更好的方法吗?
- 我的测量方法是否正确(我认为我最好应该循环它们并取该值)?
- 这些值是否可传输或取决于数据表/集合的大小?
- 在我的更新(1000000次迭代)之后,ContainsKey是最快的。这是因为我总是检查相同的 id 还是一般情况?是否有某种没有字典键值对开销的 SortedList?
结果 [1000000 次迭代*]
- 时间跨度 1 = SortedList.ContainsKey = Ø 0.65634 [238.1095] 毫秒
- 时间跨度 2 = List.Contains = Ø 0.06802 [57045.37955] 毫秒
- 时间跨度 3 = DataTable.FindByIdData(自动生成的方法)= Ø 0.31580 [1542.62345 ] ms
- Timespan 4 = DataTable.Select = Ø 0.27790 [26029.39635] ms
-
Timespan 5 = DataRowCollection.Contains = Ø 0.00638 [1202.79735] 毫秒
<前><代码>1。) 时间跨度 1:0.6913 毫秒 时间跨度 2:0,1053 毫秒 时间跨度 3:0.3279 毫秒 时间跨度 4:0,1002 毫秒 时间跨度 5:0,0056 毫秒 2.) 时间跨度 1:0.6405 毫秒 时间跨度 2:0.0588 毫秒 时间跨度 3:0.3112 毫秒 时间跨度 4:0.3872 毫秒 时间跨度 5:0,0067 毫秒 3.) 时间跨度 1:0.6502 毫秒 时间跨度 2:0.0588 毫秒 时间跨度 3:0,3092 毫秒 时间跨度 4:0.1268 毫秒 时间跨度 5:0,007 毫秒 4.) 时间跨度 1:0.6504 毫秒 时间跨度 2:0.0586 毫秒 时间跨度 3:0,3092 毫秒 时间跨度 4:0.3893 毫秒 时间跨度 5:0,0063 毫秒 5.) 时间跨度 1:0.6493 毫秒 时间跨度 2:0.0586 毫秒 时间跨度 3:0.3215 毫秒 时间跨度 4:0,386 毫秒 时间跨度 5:0,0063 毫秒 时间跨度 1:0,6913 0,6405 0,6502 0,6504 0,6493 = Ø 0,65634 时间跨度 2:0,1053 0,0588 0,0588 0,0586 0,0586 = Ø 0,06802 时间跨度 3:0,3279 0,3112 0,3092 0,3092 0,3215 = Ø 0,31580 时间跨度 4:0,1002 0,3872 0,1268 0,3893 0,3860 = Ø 0,27790 时间跨度 5:0,0056 0,0067 0,0070 0,0063 0,0063 = Ø 0,00638
为了完整起见,VB.Net 源部分:
Dim applies As Boolean
Dim clock As New System.Diagnostics.Stopwatch
clock.Start()
For i As Int32 = 1 To 1000000
applies = sortedListAC17NextClaims.ContainsKey(myClaim.idData)
Next
clock.Stop()
Dim timeSpan1 As String = "Timespan 1: " & clock.Elapsed.TotalMilliseconds.ToString & " ms"
clock.Reset()
clock.Start()
For i As Int32 = 1 To 1000000
applies = listAC17NextClaims.Contains(myClaim.idData)
Next
clock.Stop()
Dim timeSpan2 As String = "Timespan 2: " & clock.Elapsed.TotalMilliseconds.ToString & " ms"
clock.Reset()
clock.Start()
For i As Int32 = 1 To 1000000
applies = Not MyDS.AC17NextClaims.FindByIdData(myClaim.idData) Is Nothing
Next
clock.Stop()
Dim timeSpan3 As String = "Timespan 3: " & clock.Elapsed.TotalMilliseconds.ToString & " ms"
clock.Reset()
clock.Start()
For i As Int32 = 1 To 1000000
applies = MyDS.AC17NextClaims.Select("idData=" & myClaim.idData).Length > 0
Next
clock.Stop()
Dim timeSpan4 As String = "Timespan 4: " & clock.Elapsed.TotalMilliseconds.ToString & " ms"
clock.Reset()
clock.Start()
For i As Int32 = 1 To 1000000
applies = MyDS.AC17NextClaims.Rows.Contains(myClaim.idData)
Next
clock.Stop()
Dim timeSpan5 As String = "Timespan 5: " & clock.Elapsed.TotalMilliseconds.ToString & " ms"
-
更新:< /强> 我已经改变了我的结果和上面的来源。方括号中是 1000000 次迭代的值。现在结果完全不同了。现在最快的方法肯定是SortedList的ContainsKey。
-
更新2: 我忘记了使用 List.BinarySearch 的替代方法。 这对我来说似乎是最快的:
clock.Start() 对于 i 作为 Int32 = 1 到 1000000 适用 = listAC17NextClaims.BinarySearch(myClaim.idData) > -1 下一个 时钟.Stop()
只需要 219.1805 毫秒即可执行 1000000 次迭代,因此是最快的,没有 SortedList-KeyValue-Pair 的开销。 我可以使用它而无需对列表进行排序,因为 DataAdapter 使用 Order By 子句填充数据表。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
为什么不使用具有 HashTable 作为底层数据结构的集合(
Dictionary
或HashSet
)?如果键中没有冲突(如您所述),HashTables
应该提供O(1)
查找时间,并且不需要“排序”的开销。编辑:如果您只想存储密钥,则应该使用HashSet 在 .NET 3.5 及更高版本中可用。
来自 MSDN 上的 SortedList:
要以 .NET 2.0 为目标,您可以自行推出或使用预构建的,例如 Wintellect 的 Power Collections (您也可以轻松地使用源代码)。
Why don't you use a collection that has a HashTable as the underlying data structure (
Dictionary<TKey, TValue>
orHashSet<T>
)?HashTables
should provideO(1)
look up time if there are no collisions in keys (as you've stated) and doesn't need the overhead to "sort".EDIT: If you only want to store the keys, you should use HashSet<T> which is available in .NET 3.5 and above.
From MSDN on SortedList:
To target .NET 2.0, you could roll your own or use a pre-built one like Wintellect's Power Collections (you could easily just use the source as well).
在我看来,你并没有提供足够的工作来在这里获得有用的时间。您所有的时间都是亚毫秒级的,并且几乎肯定只是噪音 - 缓存、jit-ing、抢占等。
让您的集合足够大,需要几秒钟才能运行,或者至少在一个时间内运行每个测试足够多的次数。紧密循环需要几秒钟。
It doesn't seem to me that you're providing nearly enough work to get useful timings here. All your times are sub-millisecond, and are almost certainly just noise - caching, jit-ing, pre-emption, etc.
Make your collections big enough to take seconds to run, or at the very least run each test enough times in a tight loop to take seconds.
如前所述,您的代码仅运行该操作一次。通常的策略是多次运行代码(例如,进行 3 次搜索)以获得更大的数字(因此,如果 3 次搜索需要 0.9 秒,则可以说一次搜索需要 0.3 秒)。然后循环几次以允许您计算平均值(如果您愿意,可以包括标准差,以过滤掉疯狂的结果),然后最重要的是,运行一次而不注意记录时间,以便任何 JITing被执行。
另外,在不附加调试器的情况下以发布模式运行代码。
As has been noted, your code only runs the action once. A usual tactic is to run the code a number of times (say, do 3 searches) to get larger numbers (so if 3 searches take 0.9 seconds, you can say a search takes 0.3). Then loop this a few times to allow you to calculate an average (including standard deviation if you like, to filter out wild results), and then on top of that, run once without paying attention to the record time in order for any JITing to be performed.
Also, run the code in release mode with no debugger attached.