String.Starts的性能使用StringComparison.OrdinalIgnoreCase

时间:2017-08-24 05:11:04

标签: c# string string-comparison startswith

我遇到了一个奇怪的表演"神器"使用String.StartsWith。

使用OrdinalIgnoreCase的String.StartsWith似乎比使用String.StartsWith更快,而没有指定StringComparison。 (快2-4倍)

但是,使用没有StringComparison的String.Equals比使用OrdinalIgnoreCase时更快地检查相等性。 (虽然速度大致相同)

问题是为什么?为什么他们在这两种情况下表现不同?

以下是我使用的代码:

    public static void Test()
    {
        var options = new[] { "asd/klfe", "qer/jlkfe", "p33/ji", "fkjlfe", "asd/23", "bleash", "quazim", "ujv/3", "jvd/kfl" };
        Random r;

        const int trialSize = 100000;
        const int trials = 1000;
        Stopwatch swEqOp = new Stopwatch();
        Stopwatch swEq = new Stopwatch();
        Stopwatch swEqOrdinal = new Stopwatch();
        Stopwatch swStartsWith = new Stopwatch();
        Stopwatch swStartsWithOrdinal = new Stopwatch();
        for (int i = 0; i < trials; i++)
        {
            {
                r = new Random(1);
                swEqOp.Start();
                for (int j = 0; j < trialSize; j++)
                {
                    bool result = options[r.Next(options.Length)] == "asd/klfe";
                }
                swEqOp.Stop();
            }

            {
                r = new Random(1);
                swEq.Start();
                for (int j = 0; j < trialSize; j++)
                {
                    bool result = string.Equals(options[r.Next(options.Length)], "asd/klfe");
                }
                swEq.Stop();
            }

            {
                r = new Random(1);
                swEqOrdinal.Start();
                for (int j = 0; j < trialSize; j++)
                {
                    bool result = string.Equals(options[r.Next(options.Length)], "asd/klfe", StringComparison.OrdinalIgnoreCase);
                }
                swEqOrdinal.Stop();
            }

            {
                r = new Random(1);
                swStartsWith.Start();
                for (int j = 0; j < trialSize; j++)
                {
                    bool result = options[r.Next(options.Length)].StartsWith("asd/");
                }
                swStartsWith.Stop();
            }

            {
                r = new Random(1);
                swStartsWithOrdinal.Start();
                for (int j = 0; j < trialSize; j++)
                {
                    bool result = options[r.Next(options.Length)].StartsWith("asd/",StringComparison.OrdinalIgnoreCase);
                }
                swStartsWithOrdinal.Stop();
            }

        }

        //DEBUG with debugger attached. Release without debugger attached. AnyCPU both cases.

        //DEBUG : 1.54      RELEASE : 1.359
        Console.WriteLine("Equals Operator: " + swEqOp.ElapsedMilliseconds / 1000d);

        //DEBUG : 1.498      RELEASE : 1.349  <======= FASTEST EQUALS
        Console.WriteLine("String.Equals: " + swEq.ElapsedMilliseconds / 1000d);

        //DEBUG : 1.572      RELEASE : 1.405
        Console.WriteLine("String.Equals OrdinalIgnoreCase: " + swEqOrdinal.ElapsedMilliseconds / 1000d);

        //DEBUG : 14.234      RELEASE : 9.914
        Console.WriteLine("String.StartsWith: " + swStartsWith.ElapsedMilliseconds / 1000d);

        //DEBUG : 7.956      RELEASE : 3.953  <======= FASTEST StartsWith
        Console.WriteLine("String.StartsWith OrdinalIgnoreCase: " + swStartsWithOrdinal.ElapsedMilliseconds / 1000d);

    }

2 个答案:

答案 0 :(得分:2)

public Boolean StartsWith(String value, StringComparison comparisonType)中的实现似乎有所不同:

        switch (comparisonType) {
            case StringComparison.CurrentCulture:
                return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None);

            case StringComparison.CurrentCultureIgnoreCase:
                return CultureInfo.CurrentCulture.CompareInfo.IsPrefix(this, value, CompareOptions.IgnoreCase); 

            case StringComparison.InvariantCulture:
                return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.None); 

            case StringComparison.InvariantCultureIgnoreCase:
                return CultureInfo.InvariantCulture.CompareInfo.IsPrefix(this, value, CompareOptions.IgnoreCase);

            case StringComparison.Ordinal:
                if( this.Length < value.Length) { 
                    return false; 
                }
                return (nativeCompareOrdinalEx(this, 0, value, 0, value.Length) == 0); 

            case StringComparison.OrdinalIgnoreCase:
                if( this.Length < value.Length) {
                    return false; 
                }

                return (TextInfo.CompareOrdinalIgnoreCaseEx(this, 0, value, 0, value.Length, value.Length) == 0); 

            default: 
                throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
        }

使用的默认比较是:

#if FEATURE_CORECLR
                              StringComparison.Ordinal);
#else
                              StringComparison.CurrentCulture); 
#endif

答案 1 :(得分:1)

与String.StartsWith(由Enigmativity指出)不同,如果没有指定String.Equals,则默认情况下不使用任何StringComparison。相反,它使用自己的自定义实现,您可以在下面的链接中看到: https://referencesource.microsoft.com/#mscorlib/system/string.cs,11648d2d83718c5e

这比Ordinal Comparison快一点。

但重要的是要注意,如果您希望比较之间保持一致,请将String.Equals和String.StartsWith与StringComparison一起使用,否则它们不会像您期望的那样运行。