【问题标题】:c# Fastest way to compare stringsc#最快的比较字符串的方法
【发布时间】:2013-10-17 20:18:57
【问题描述】:

我注意到了

string1.Length == string2.Length && string1 == string2

在大字符串上比仅仅快一点

string1 == string2

这是真的吗?这是在比较实际字符串之前比较大字符串长度的好习惯吗?

【问题讨论】:

  • 您是如何注意到的?有任何事实可以支持此通知吗?有任何样品测试可以证明吗?
  • @MishaZaslavsky 很难测试,如果“测试”也意味着“生成有意义的测试输入”。
  • 你怎么知道string1 == string2不先检查长度?
  • 除非你有一个基准表明一个比另一个更快使用你的特定数据,这里的正确答案是“使用更具可读性的那个”。
  • @ConradFrix 确实如此;)

标签: c# string performance


【解决方案1】:

strings 运算符 equals 在比较字符之前进行长度检查。所以你不要用这个技巧保存内容的比较。您可能仍会节省一些 CPU 周期,因为您的长度检查假定字符串不为空,而 BCL 必须检查这一点。因此,如果大部分时间长度不相等,您将短路一些指令。

不过,我在这里可能是错的。也许操作员会内联并优化检查。谁肯定知道? (衡量的人。)

如果您关心保存每个周期,您可能应该首先使用不同的策略。也许托管代码甚至不是正确的选择。鉴于此,我建议使用较短的形式,而不是使用附加检查。

【讨论】:

  • So if the lengths are not equal most of the time, you will short-circuit a few instructions. 现在是统计问题
  • 是的,这很可能是为什么我注意到循环一百万次时性能差异很小的原因,它只是跳过了对两个值的空检查,节省了几个周期。差异太小了,无法在此处添加额外的一行。
  • 根据参考资料,string 运算符重载== 调用String.Equals(),这只会引发更多关于观察到的性能差异的问题......
  • String.Lengths 上使用== 时,您可能还避免了方法调用的开销。
【解决方案2】:

String.Equality Operator==内部调用string.Equals,所以使用框架提供的string.Equals==。它已经足够优化了。

它首先比较引用,然后是长度,然后是实际字符。

可以找到源代码here

代码:(来源:http://www.dotnetframework.org/default.aspx/4@0/4@0/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/clr/src/BCL/System/String@cs/1305376/String@cs

// Determines whether two strings match.
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public override bool Equals(Object obj) {
    if (this == null)                        //this is necessary to guard against reverse-pinvokes and
        throw new NullReferenceException();  //other callers who do not use the callvirt instruction

    String str = obj as String;
    if (str == null)
        return false;

    if (Object.ReferenceEquals(this, obj))
        return true;

    return EqualsHelper(this, str);
}

[System.Security.SecuritySafeCritical]  // auto-generated
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private unsafe static bool EqualsHelper(String strA, String strB)
{
    Contract.Requires(strA != null);
    Contract.Requires(strB != null);
    int length = strA.Length;
    if (length != strB.Length) return false;

    fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar)
    {
        char* a = ap;
        char* b = bp;

        // unroll the loop
#if AMD64
        // for AMD64 bit platform we unroll by 12 and
        // check 3 qword at a time. This is less code
        // than the 32 bit case and is shorter
        // pathlength

        while (length >= 12)
        {
            if (*(long*)a     != *(long*)b) break;
            if (*(long*)(a+4) != *(long*)(b+4)) break;
            if (*(long*)(a+8) != *(long*)(b+8)) break;
            a += 12; b += 12; length -= 12;
        }
 #else
        while (length >= 10)
        {
            if (*(int*)a != *(int*)b) break;
            if (*(int*)(a+2) != *(int*)(b+2)) break;
            if (*(int*)(a+4) != *(int*)(b+4)) break;
            if (*(int*)(a+6) != *(int*)(b+6)) break;
            if (*(int*)(a+8) != *(int*)(b+8)) break;
            a += 10; b += 10; length -= 10;
        }
  #endif

        // This depends on the fact that the String objects are
        // always zero terminated and that the terminating zero is not included
        // in the length. For odd string sizes, the last compare will include
        // the zero terminator.
        while (length > 0)
        {
            if (*(int*)a != *(int*)b) break;
            a += 2; b += 2; length -= 2;
        }

        return (length <= 0);
    }
}

【讨论】:

  • 它只是比较引用,除非你通过特殊选项,无论如何。那是因为字符串实习生池。
  • +1: == 操作员使用int i = strA.Length; if (i != strB.Length) { return false; }
  • 不,它比较值。除非您先将字符串转换为对象并使用==
  • 为什么投反对票?向下滚动到 EqualsHelpers 以查看实际情况!
  • @It'sNotALie 不,==比较参考与值。 C# 不是 Java
【解决方案3】:

我的测试结果

将 10000 个字符串与 10000 个长度相同的其他字符串进行比较 (256)

时间(s1 == s2):32536889 滴答声

时间 (s1.Length == s2.Length) && (s1 == s2): 37380529 滴答声

将 10000 个字符串与 10000 个其他字符串进行比较随机长度最大 256

时间(s1 == s2):27223517 滴答声

时间 (s1.Length == s2.Length) && (s1 == s2): 23419529 滴答声

将 10000 个字符串与 10000 个其他字符串进行比较 随机长度 min 256 max 512

时间(s1 == s2):28904898 滴答声

时间 (s1.Length == s2.Length) && (s1 == s2): 25442710 滴答声

我发现令人难以置信的是,比较 10000 个相等长度的字符串比比较相同数量的更大数据需要更长的时间。

所有这些测试都是用完全相同的数据完成的。

【讨论】:

  • 我有点晚了,但是等长字符串需要更长的时间的原因是因为 == 运算符首先检查它们是否为空,然后检查它们是否相等,然后再检查内容是否完全相同的。如果长度相同,则继续;如果它们不同,它就会停在那里。
【解决方案4】:

对于我们中间的极客,here's a page 在基准测试numerous ways to compare strings 方面做得很好。

简而言之,最快的方法似乎是 CompareOrdinal:

if (string.CompareOrdinal(stringsWeWantToSeeIfMatches[x], stringsWeAreComparingAgainst[x]) == 0)
{
//they're equal
}

第二个最好的方法似乎是使用字典或哈希集,将“键”作为要比较的字符串。

读起来很有趣。

【讨论】:

  • 链接似乎已损坏;它现在指的是当前页面。
  • 谢谢。我已经修复了链接。
  • 这个信息有多久了? DotNet Core 还是这样吗?
【解决方案5】:

根据ILSpy,字符串==操作符定义为:

public static bool operator ==(string a, string b)
{
    return string.Equals(a, b);
}

定义为

public static bool Equals(string a, string b)
{
    return a == b || (a != null && b != null && a.Length == b.Length && string.EqualsHelper(a, b));
}

我假设第一个a == b 实际上是一个引用相等检查(ILSpy 只是将它呈现为==),否则这将是一个无限递归的方法。

这意味着== 在实际比较它们的字符之前已经检查了字符串的长度。

【讨论】:

  • 这是 ILSPy 为我带来的结果(.NET 4,长度检查在 EqualsHelper):public static bool Equals(string a, string b) { return a == b || (a != null &amp;&amp; b != null &amp;&amp; string.EqualsHelper(a, b)); }
  • @TimSchmelter 你在看什么版本的程序集?发布的代码来自 4.0.0.0?在 2.0.0.0 我看到 return (value != null || this == null) &amp;&amp; string.EqualsHelper(this, value);.
  • @TimSchmelter 这确实很奇怪。我可以清楚地看到 IL 中对System.String::get_Length() 的调用。是的,我在 EqualsHelper 中看到 另一个 长度检查。
【解决方案6】:

在终止的字符串中,开始比较字符是有意义的,因为如果不迭代所有字符就无法计算字符串长度,并且比较可能会提前退出。

对于按长度计算的字符串,应该首先比较长度,如果您正在测试字节相等性。如果不检索长度,您甚至无法开始访问字符数据,因为长度可能为零。

如果您进行关系比较,知道长度不同并不能告诉您结果应该是正数还是负数。在文化感知比较中,相等的字符串并不意味着相等的长度。因此,对于这两者,您只需比较数据即可。

如果operator==(string, string) 只是委托给一个关系比较,你不会期望它比较长度。因此,在进行比较之前检查长度可能是一个好处。但似乎框架确实从长度检查开始。

【讨论】:

    【解决方案7】:

    我想说第一个更快是string1.Length == string2.Length 的结果是假的。由于采用了短路评估 (SCE),因此不会对字符串进行实际比较,这可能会节省您的时间。

    但是,如果字符串相等,第一个比较慢,因为它会先检查长度,然后执行与第二个相同的操作。

    有关 &amp;&amp; 运算符和 SCE 的信息,请参阅 http://msdn.microsoft.com/en-us/library/2a723cdk.aspx

    【讨论】:

      【解决方案8】:

      正如我承诺的那样,我用秒表编写了一个简短的代码 - 你可以复制粘贴它并尝试不同的字符串并查看差异

      class Program
      {
          static void Main(string[] args)
          {
              string str1 = "put the first value";
              string str2 = "put the second value";
              CompareTwoStringsWithStopWatch(str1, str2); //Print the results.
          }
      
          private static void CompareTwoStringsWithStopWatch(string str1, string str2)
          {
              Stopwatch stopwatch = new Stopwatch();
      
              stopwatch.Start();
              for (int i = 0; i < 99999999; i++)
              {
                  if (str1.Length == str2.Length && str1 == str2)
                  {
                      SomeOperation();
                  }
              }
              stopwatch.Stop();
      
              Console.WriteLine("{0}. Time: {1}", "Result for: str1.Length == str2.Length && str1 == str2", stopwatch.Elapsed);
              stopwatch.Reset();
      
              stopwatch.Start();
              for (int i = 0; i < 99999999; i++)
              {
                  if (str1 == str2)
                  {
                      SomeOperation();
                  }
              }
              stopwatch.Stop();
      
              Console.WriteLine("{0}. Time: {1}", "Result for: str1 == str2", stopwatch.Elapsed);
          }
      
          private static int SomeOperation()
          {
              var value = 500;
              value += 5;
      
              return value - 300;
          }
      }
      

      我的结论:

      1. 当我检查一些字符串(短字符串和长字符串)时,我发现所有结果几乎相同。因此,第一个 if(带有长度检查)在 2/3 中较慢。
      2. 您在 Object 类中有一个 Equals 方法,只需使用它即可:)
      3. 您可以尝试一下,也可以给我们结果:)

      【讨论】:

        【解决方案9】:

        如果您希望字符串在大多数情况下的长度不同,您可以比较它们的长度AND,然后使用string.Compare 比较字符串本身。通过这样做,我获得了近 50% 的性能提升:

        if (str1.Length == str2.Length)
        {
            if (string.Compare(str1, str2, StringComparison.Ordinal) == 0)
            {
               doSomething()
            }
        }
        

        在这种情况下,我希望字符串几乎总是不同,我认为 str1.Lenght 比比较实际字符串便宜得多。如果它们大小相等,我会比较它们。

        编辑:忘记我说的话。只需使用== 并快乐。

        【讨论】:

        • String 的== 运算符也首先比较String.Length
        • 所以我必须检查为什么我得到了这个性能改进。也许是另一回事。
        猜你喜欢
        • 2019-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-30
        • 2011-01-04
        • 2020-04-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多