String.StartsWith 使用 StringComparison.OrdinalIgnoreCase 的表现
Performance of String.StartsWith using StringComparison.OrdinalIgnoreCase
我 运行 进入 st运行ge 性能 "artifact" 与 String.StartsWith。
似乎 String.StartsWith 使用 OrdinalIgnoreCase 比使用 String.StartsWith 而不指定 StringComparison 更快。 (快 2-4 倍)
但是,与使用 OrdinalIgnoreCase 相比,使用没有 StringComparison 的 String.Equals 检查相等性更快。 (虽然速度都差不多)
问题是为什么?为什么他们在这两种情况下表现不同?
这是我使用的代码:
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);
}
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
因此,与 String.StartsWith(如 Enigmativity 所指出的)不同,如果指定了 none,String.Equals 默认情况下不使用任何 StringComparison。相反,它使用自己的自定义实现,您可以在下面的 link 中看到:
https://referencesource.microsoft.com/#mscorlib/system/string.cs,11648d2d83718c5e
这比序数比较稍快。
但需要注意的是,如果您想要比较之间的一致性,请将 String.Equals 和 String.StartsWith 与 StringComparison 一起使用,否则它们不会按您预期的方式运行。
我 运行 进入 st运行ge 性能 "artifact" 与 String.StartsWith。
似乎 String.StartsWith 使用 OrdinalIgnoreCase 比使用 String.StartsWith 而不指定 StringComparison 更快。 (快 2-4 倍)
但是,与使用 OrdinalIgnoreCase 相比,使用没有 StringComparison 的 String.Equals 检查相等性更快。 (虽然速度都差不多)
问题是为什么?为什么他们在这两种情况下表现不同?
这是我使用的代码:
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);
}
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
因此,与 String.StartsWith(如 Enigmativity 所指出的)不同,如果指定了 none,String.Equals 默认情况下不使用任何 StringComparison。相反,它使用自己的自定义实现,您可以在下面的 link 中看到: https://referencesource.microsoft.com/#mscorlib/system/string.cs,11648d2d83718c5e
这比序数比较稍快。
但需要注意的是,如果您想要比较之间的一致性,请将 String.Equals 和 String.StartsWith 与 StringComparison 一起使用,否则它们不会按您预期的方式运行。