2017-08-24 1 views
6

String.StartsWithを使用して奇妙なパフォーマンスの「アーティファクト」が発生しました。StringComparison.OrdinalIgnoreCaseを使用したString.Startsのパフォーマンス

OrdinalIgnoreCaseを使用しているString.Startsが、String.Componentを指定せずにString.StartsWithを使用するよりも高速であるようです。 (2-4倍高速)

ただし、OrdinalIgnoreCaseを使用する場合よりもStringComparisonを使用しないString.Equalsを使用する方が、等価性の検査が高速になります。 (すべてがほぼ同じスピードですが)

質問はなぜですか?なぜ彼らは2つのケースで異なった動作をしますか?ここで

は私が使っていたコードです。

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); 

    } 

答えて

1

String.StartsWith(Enigmativityによって指摘されているように)と違って、String.Equa lsが指定されていない場合、デフォルトでStringComparisonは使用されません。代わりに、独自のカスタム実装を使用しています。 https://referencesource.microsoft.com/#mscorlib/system/string.cs,11648d2d83718c5e

これは、序数比較よりやや速いです。

しかし、比較の間に一貫性を持たせたい場合は、String.EqualsとString.StartsWithをStringComparisonと共に使用するか、期待通りに動作していないことに注意することが重要です。

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 
+0

序数を使うとなぜ速くなるのか分かりません - なぜString.Equalsの動作が違うのか分かりません... – MineR

+0

String.Equalsを見ると、String.Equalsを見ても実際にStringComparisonは使用されません。指定された代わりに特定の実装を使用します。 – MineR

関連する問題