【问题标题】:Why is function isprefix faster than Startswith in C#?为什么函数是前缀比 C# 中的 Startswith 快?
【发布时间】:2010-10-17 14:24:28
【问题描述】:

有人知道为什么 C# (.NET) 的 StartsWith 函数比 IsPrefix 慢很多吗?

【问题讨论】:

    标签: .net performance string startswith


    【解决方案1】:

    我认为它主要是获取线程的当前文化。

    如果您将 Marc 的测试更改为使用 String.StartsWith 的这种形式:

        Stopwatch watch = Stopwatch.StartNew();
        CultureInfo cc = CultureInfo.CurrentCulture;
        for (int i = 0; i < LOOP; i++)
        {
            if (s1.StartsWith(s2, false, cc)) chk++;
        }
        watch.Stop();
        Console.WriteLine(watch.ElapsedMilliseconds + "ms; chk: " + chk);
    

    离我们更近了。

    如果您使用s1.StartsWith(s2, StringComparison.Ordinal),它比使用CompareInfo.IsPrefix 快​​很多(当然取决于CompareInfo)。在我的盒子上,结果是(不科学):

    • s1.StartsWith(s2): 6914ms
    • s1.StartsWith(s2, false,culture): 5568ms
    • compare.IsPrefix(s1, s2): 5200ms
    • s1.StartsWith(s2, StringComparison.Ordinal): 1393ms

    显然这是因为它实际上只是在每个点上比较 16 位整数,这非常便宜。如果你想要文化敏感检查,性能对你来说特别重要,那就是我会使用的重载。

    【讨论】:

      【解决方案2】:

      好问题;对于测试,我得到:

      9156ms; chk: 50000000
      6887ms; chk: 50000000
      

      测试台:

      using System;
      using System.Diagnostics;
      using System.Globalization;    
      
      class Program
      {
          static void Main()
          {
              string s1 = "abcdefghijklmnopqrstuvwxyz", s2 = "abcdefg";
      
              const int LOOP = 50000000;
              int chk = 0;
              Stopwatch watch = Stopwatch.StartNew();
              for (int i = 0; i < LOOP; i++)
              {
                  if (s1.StartsWith(s2)) chk++;
              }
              watch.Stop();
              Console.WriteLine(watch.ElapsedMilliseconds + "ms; chk: " + chk);
      
              chk = 0;
              watch = Stopwatch.StartNew();
      
              CompareInfo ci = CultureInfo.CurrentCulture.CompareInfo;
              for (int i = 0; i < LOOP; i++)
              {
                  if (ci.IsPrefix(s1, s2)) chk++;
              }
              watch.Stop();
              Console.WriteLine(watch.ElapsedMilliseconds + "ms; chk: " + chk);
          }
      }
      

      【讨论】:

        【解决方案3】:

        StartsWith 在内部调用 IsPrefix。它在调用 IsPrefix 之前分配文化信息。

        【讨论】:

          【解决方案4】:

          查看 IsPrefix 的来源。问题是 - 在某些情况下,它会比 StartsWith 慢,因为它实际上使用 StartsWith 并且执行的操作很少。

              [System.Security.SecuritySafeCritical]  // auto-generated
              public unsafe virtual bool IsPrefix(String source, String prefix, CompareOptions options)
              { 
                  if (source == null || prefix == null) {
                      throw new ArgumentNullException((source == null ? "source" : "prefix"), 
                          Environment.GetResourceString("ArgumentNull_String")); 
                  }
                  Contract.EndContractBlock(); 
                  int prefixLen = prefix.Length;
          
                  if (prefixLen == 0)
                  { 
                      return (true);
                  } 
          
                  if (options == CompareOptions.OrdinalIgnoreCase)
                  { 
                      return source.StartsWith(prefix, StringComparison.OrdinalIgnoreCase);
                  }
          
                  if (options == CompareOptions.Ordinal) 
                  {
                      return source.StartsWith(prefix, StringComparison.Ordinal); 
                  } 
          
                  if ((options & ValidIndexMaskOffFlags) != 0) { 
                      throw new ArgumentException(Environment.GetResourceString("Argument_InvalidFlag"), "options");
                  }
          
          
                  // to let the sorting DLL do the call optimization in case of Ascii strings, we check if the strings are in Ascii and then send the flag RESERVED_FIND_ASCII_STRING  to
                  // the sorting DLL API SortFindString so sorting DLL don't have to check if the string is Ascii with every call to SortFindString. 
          
                  return (InternalFindNLSStringEx(
                              m_dataHandle, m_handleOrigin, m_sortName, 
                              GetNativeCompareFlags(options) | Win32Native.FIND_STARTSWITH | ((source.IsAscii() && prefix.IsAscii()) ? RESERVED_FIND_ASCII_STRING : 0),
                              source, source.Length, 0, prefix, prefix.Length) > -1);
              }
          

          【讨论】:

            猜你喜欢
            • 2012-08-24
            • 1970-01-01
            • 1970-01-01
            • 2021-04-05
            • 2013-11-21
            • 1970-01-01
            • 2012-10-16
            • 1970-01-01
            • 2015-11-02
            相关资源
            最近更新 更多