Why is function isprefix faster than Startswith in C#?

前端 未结 4 694
一生所求
一生所求 2021-02-04 03:10

Does anyone know why C# (.NET)\'s StartsWith function is considerably slower than IsPrefix?

相关标签:
4条回答
  • 2021-02-04 03:21

    I think it's mostly fetching the thread's current culture.

    If you change Marc's test to use this form of 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);
    

    it comes a lot closer.

    If you use s1.StartsWith(s2, StringComparison.Ordinal) it's a lot faster than using CompareInfo.IsPrefix (depending on the CompareInfo of course). On my box the results are (not scientifically):

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

    Obviously that's because it's really just comparing 16 bit integers at each point, which is pretty cheap. If you don't want culture-sensitive checking, and performance is particularly important to you, that's the overload I'd use.

    0 讨论(0)
  • 2021-02-04 03:21

    StartsWith calls IsPrefix internally. It assigns culture info before calling IsPrefix.

    0 讨论(0)
  • 2021-02-04 03:32

    Check out the source of IsPrefix. The thing is - in some cases, it's going to be slower than StartsWith just because it actually uses StartsWith and does few more operations.

        [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);
        }
    
    0 讨论(0)
  • 2021-02-04 03:40

    Good question; for a test, I get:

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

    Test rig:

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题