【问题标题】:Performance-comparison of Sort() and BinarySearch() with Integers/StringsSort() 和 BinarySearch() 与整数/字符串的性能比较
【发布时间】:2010-11-15 09:55:01
【问题描述】:

最初我想问对整数进行排序是否比对字符串进行排序更快。 但我自己已经回答了这个问题,我对巨大的差异感到惊讶。 为什么排序和 BinarySearch 整数与字符串相比要快得多?

使用 1.000.000 Int32/Strings 的 (VB.Net) 测试:

Private Function CheckIntBinarySearch() As TimeSpan
    Dim watch As New System.Diagnostics.Stopwatch()
    Dim rnd As New Random(Date.Now.Millisecond)
    Dim intCol1 As New List(Of Int32)
    Dim intCol2 As New List(Of Int32)
    Dim contains As Int32
    For i As Int32 = 1 To 1000000
        intCol1.Add(rnd.Next(1, 1000000))
    Next
    For i As Int32 = 1 To 1000000
        intCol2.Add(rnd.Next(1, 1000000))
    Next
    Me.output.WriteLine("Integers sorting ...")
    watch.Start()
    intCol1.Sort()
    watch.Stop()
    Me.output.WriteLine("Sorting finished: " & watch.Elapsed.TotalSeconds & " seconds elapsed.")

    Me.output.WriteLine("Integers BinarySearch ...")
    watch.Start()
    For Each Val As Int32 In intCol2
        If intCol1.BinarySearch(Val) > -1 Then contains += 1
    Next
    watch.Stop()
    Me.output.WriteLine("BinarySearch finished(contains " & contains & "): " & watch.Elapsed.TotalSeconds & " seconds elapsed.")
    Return watch.Elapsed
End Function

Private Function CheckStringBinarySearch() As TimeSpan
    Dim watch As New System.Diagnostics.Stopwatch()
    Dim rnd As New Random(Date.Now.Millisecond)
    Dim stringCol1 As New List(Of String)
    Dim stringCol2 As New List(Of String)
    Dim contains As Int32
    For i As Int32 = 1 To 1000000
        stringCol1.Add(rnd.Next(1, 1000000).ToString)
    Next
    For i As Int32 = 1 To 1000000
        stringCol2.Add(rnd.Next(1, 1000000).ToString)
    Next
    Me.output.WriteLine("Strings sorting ...")
    watch.Start()
    stringCol1.Sort()
    watch.Stop()
    Me.output.WriteLine("Sorting finished: " & watch.Elapsed.TotalSeconds & " seconds elapsed.")
    Me.output.WriteLine("Strings BinarySearch ...")
    watch.Start()
    For Each Val As String In stringCol2
        If stringCol1.BinarySearch(Val) > -1 Then contains += 1
    Next
    watch.Stop()
    Me.output.WriteLine("BinarySearch finished(contains " & contains & "): " & watch.Elapsed.TotalSeconds & " seconds elapsed.")
    Return watch.Elapsed
End Function

检查性能 5 次:

For i As Int32 = 1 To 5
   intChecks.Add(CheckIntBinarySearch())
Next
For i As Int32 = 1 To 5
   stringChecks.Add(CheckStringBinarySearch())
Next

输出:

    1.)Integers sorting ...
    Sorting finished: 0,2292863 seconds elapsed.
    Integers BinarySearch ...
    BinarySearch finished(contains 630857): 0,9365744 seconds elapsed.
    2.)Integers sorting ...
    Sorting finished: 0,2287382 seconds elapsed.
    Integers BinarySearch ...
    BinarySearch finished(contains 632600): 0,9053134 seconds elapsed.
    3.)Integers sorting ...
    Sorting finished: 0,2318829 seconds elapsed.
    Integers BinarySearch ...
    BinarySearch finished(contains 631475): 0,9038594 seconds elapsed.
    4.)Integers sorting ...
    Sorting finished: 0,2308994 seconds elapsed.
    Integers BinarySearch ...
    BinarySearch finished(contains 632346): 0,9011047 seconds elapsed.
    5.)Integers sorting ...
    Sorting finished: 0,2266423 seconds elapsed.
    Integers BinarySearch ...
    BinarySearch finished(contains 632982): 0,893541 seconds elapsed.
    1.)Strings sorting ...
    Sorting finished: 6,5661916 seconds elapsed.
    Strings BinarySearch ...
    BinarySearch finished(contains 632579): 12,9545657 seconds elapsed.
    2.)Strings sorting ...
    Sorting finished: 6,5641975 seconds elapsed.
    Strings BinarySearch ...
    BinarySearch finished(contains 631478): 13,0184132 seconds elapsed.
    3.)Strings sorting ...
    Sorting finished: 6,4281382 seconds elapsed.
    Strings BinarySearch ...
    BinarySearch finished(contains 631775): 12,7684214 seconds elapsed.
    4.)Strings sorting ...
    Sorting finished: 6,9455087 seconds elapsed.
    Strings BinarySearch ...
    BinarySearch finished(contains 631478): 13,7057234 seconds elapsed.
    5.)Strings sorting ...
    Sorting finished: 6,6707111 seconds elapsed.
    Strings BinarySearch ...
    BinarySearch finished(contains 632346): 13,0493649 seconds elapsed.
  • Int32 平均排序:0,22948982
  • String 平均排序:6,63494942
  • Int32 BinarySearch 平均:0,90807858
  • String BinarySearch 平均:13,09929772

结论:

  • 整数排序比字符串排序快 29
  • BinarySearch Integers 比 BinarySearch Strings 快 14,4

为什么? 考虑拥有大量“字符串整数”(“1”,“2”,“3”,...)。在对它们进行排序和搜索之前将它们解析为整数会更好吗?将字符串解析为整数的成本是多少?好的,我想这是另一个问题。

【问题讨论】:

  • 您还可以使用字符串变体获得不同的排序,100010 在词法排序中位于 2 之前。
  • 是的,我知道,但是当我 MyStringCollection.BinarySearch("1") 时,如果集合使用 StringComparer 排序,我会得到正确的结果。
  • 如果您所做的只是尝试检查成员资格,您可能需要研究 HashSet - 它通常比排序和二进制搜索更快,尤其是对于字符串

标签: .net vb.net performance generics


【解决方案1】:

字符串比较存在许多整数比较转义的问题。

  1. 考虑一下如何编写字符串比较。首先,您可以检查引用是否为空,然后您可以检查两个字符串的长度是否匹配,然后您可以一次比较每个数字。这至少是 2 次比较,其中整数只使用一次。
  2. 考虑比较“111112”和“111113”。对于字符串,在得到结果之前需要进行 6 次比较(每个字符一次)。对于整数,它只是一种比较。
  3. 可以优化整数比较和数组以使用特定于寄存器的指令,因此可能会直接在 CPU 缓存内进行大量排序/搜索 - 对于字符串,可能需要调用多个方法来访问字符、检查长度等。李>
  4. 如果您需要特定于语言环境的比较,或者可以处理“ß”与“ss”或“æ”与“ae”相同的比较,您甚至不能使用长度优化。
  5. 字符串是引用类型,整数是值类型,因此可能存在取消引用。

此列表并不详尽,但至少可以了解问题。

顺便说一句,不影响性能 - 在比较字符串时,您应该注意 "12"

【讨论】:

  • 即使长度不相等,仍然需要比较字符串。这是因为字符序列可能匹配单个字符(“æ”/“ae”、“ß”/“ss”等)。当长度不匹配时,只有序号字符串比较可以快速返回 false(显然仅适用于 Equals,不适用于 CompareTo)。
【解决方案2】:

计算机可以用一条指令比较 2 个整数,这只需要几纳秒。

比较两个字符串是另一回事,其中涉及:

  1. 获取字符串A的长度
  2. 获取字符串B的长度
  3. 检查长度,如果它们都是 零,认为字符串相等。
  4. 获取字符串A的位置
  5. 获取字符串B的位置
  6. 开始比较,按字符 字符 A 对 B。(一些 处理器可以在一个 指令,但它是一个 指导。)

比较字符串可能出现类似于比较整数,但对于计算机,它要困难得多,正如您的测试结果显示的那样

【讨论】:

    【解决方案3】:

    您正在使用文化感知比较对字符串进行排序。 请记住,并非世界上所有的语言都同意字母表。

    这意味着每当比较两个字符串时,.NET 都会查找用户当前的文化,然后使用该语言的规则比较字符串。这是一个重要的操作 - 例如“ae”和“æ”被认为是相等的。

    要加快字符串的排序,请使用:

     stringCol1.Sort(StringComparer.Ordinal)
    

    通过此更改,您将消除大部分开销(所有文化感知的东西),但随后 smirkingman 的答案仍然适用于简单的字符串比较。

    要了解默认字符串比较的复杂程度,请查看Unicode Collation Algorithm

    【讨论】:

      【解决方案4】:

      问题是字符串是引用类型,整数是值类型。

      对于每个字符串,必须取消引用字符串的实际位置并进行比较。

      对于整数,值已经存在并且比较便宜得多。

      【讨论】:

      • 额外的取消引用只是成本的一​​小部分,我怀疑你会看到整数和装箱整数之间的很大差异。字符串比较更昂贵,因为它们涉及特定于文化的字符 Unicode 字符权重表和各种复杂的东西。
      • 和按位比较;不要忘记那个。千万不要忘记整数总是 4 个字节。字符串没有预先确定的长度,这无济于事。而且,因为它是 utf-16,所以一个三位数的整数已经比普通整数需要更多的字节数。
      • 根本不是按位比较;看看我的回答。
      • @Daniel - 啊,是的,你是对的。甚至比这更糟。按位删除。当然,正如你所说,Ordinal 会的。
      【解决方案5】:

      正如其他人所说,比较整数是单个 sub 指令,而要比较两个字符串,它们必须通过引用压入堆栈,调用函数,执行函数入口代码,然后循环执行字符,执行的函数退出代码,然后返回,然后是一条sub 指令

      如果您将其暂停几次,您就会看到。几乎每次暂停时,它都会出现在 string-compare 函数中,告诉您该函数占了 90% 或更多的时间。

      【讨论】:

        猜你喜欢
        • 2013-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-21
        相关资源
        最近更新 更多