Консультант приехал вчера, и так или иначе тема строк подошла. Он упомянул, что заметил это для строк меньше, чем определенная длина, Contains
на самом деле быстрее, чем StartsWith
. Я должен был видеть его своими двумя глазами, таким образом, я записал немного приложения и конечно же, Contains
быстрее!
Как это возможно?
DateTime start = DateTime.MinValue;
DateTime end = DateTime.MinValue;
string str = "Hello there";
start = DateTime.Now;
for (int i = 0; i < 10000000; i++)
{
str.Contains("H");
}
end = DateTime.Now;
Console.WriteLine("{0}ms using Contains", end.Subtract(start).Milliseconds);
start = DateTime.Now;
for (int i = 0; i < 10000000; i++)
{
str.StartsWith("H");
}
end = DateTime.Now;
Console.WriteLine("{0}ms using StartsWith", end.Subtract(start).Milliseconds);
Выводы:
726ms using Contains
865ms using StartsWith
Я попробовал его более длинными строками также!
Попробуйте использовать Секундомер
для измерения скорости вместо проверки DateTime
.
Секундомер против использования System.DateTime.Now для временных событий
Я думаю, что ключевыми являются следующие важные части, выделенные жирным шрифтом:
Содержит
:
Этот метод выполняет порядковый номер (с учетом регистра и нечувствительность к культуре ) сравнение.
StartsWith
:
Этот метод выполняет слово (с учетом регистра и с учетом культуры ) сравнение с использованием текущей культуры.
Я думаю, что ключом является порядковое сравнение , которое сводится к:
Порядковая сортировка сравнивает строки на основе на числовое значение каждого Char объект в строке. Порядковый сравнение происходит автоматически с учетом регистра, потому что строчные буквы и заглавные версии символа имеют разные кодовые точки. Тем не мение, если в вашем приложение, вы можете указать порядковое сравнение без учета регистра.Это эквивалентно преобразованию строку в верхний регистр с помощью инвариантная культура, а затем выполнение порядковое сравнение по результату.
Ссылки:
http://msdn.microsoft.com/en-us/library/system.string.aspx
http://msdn.microsoft.com/en-us/library/dy85x1sa. aspx
http://msdn.microsoft.com/en-us/library/baketfxw.aspx
Используя Reflector, вы можете увидеть код для двух:
public bool Contains(string value)
{
return (this.IndexOf(value, StringComparison.Ordinal) >= 0);
}
public bool StartsWith(string value, bool ignoreCase, CultureInfo culture)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
if (this == value)
{
return true;
}
CultureInfo info = (culture == null) ? CultureInfo.CurrentCulture : culture;
return info.CompareInfo.IsPrefix(this, value,
ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
}
Я разобрался. Это потому, что StartsWith
зависит от языка и региональных параметров, а Contains - нет. По сути, это означает, что StartsWith
должен выполнять больше работы.
FWIW, вот мои результаты на Mono с приведенным ниже (исправленным) тестом:
1988.7906ms using Contains
10174.1019ms using StartsWith
Я был бы рад увидеть результаты людей на MS, но моя главная мысль в том, что все сделано правильно (и предполагая аналогичные оптимизации), я думаю, что StartsWith
должен быть медленнее:
using System;
using System.Diagnostics;
public class ContainsStartsWith
{
public static void Main()
{
string str = "Hello there";
Stopwatch s = new Stopwatch();
s.Start();
for (int i = 0; i < 10000000; i++)
{
str.Contains("H");
}
s.Stop();
Console.WriteLine("{0}ms using Contains", s.Elapsed.TotalMilliseconds);
s.Reset();
s.Start();
for (int i = 0; i < 10000000; i++)
{
str.StartsWith("H");
}
s.Stop();
Console.WriteLine("{0}ms using StartsWith", s.Elapsed.TotalMilliseconds);
}
}
Я покопался в Reflector и нашел потенциальный ответ:
Contains:
return (this.IndexOf(value, StringComparison.Ordinal) >= 0);
StartsWith:
...
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:
return ((this.Length >= value.Length) && (nativeCompareOrdinalEx(this, 0, value, 0, value.Length) == 0));
case StringComparison.OrdinalIgnoreCase:
return ((this.Length >= value.Length) && (TextInfo.CompareOrdinalIgnoreCaseEx(this, 0, value, 0, value.Length, value.Length) == 0));
}
throw new ArgumentException(Environment.GetResourceString("NotSupported_StringComparison"), "comparisonType");
И есть некоторые перегрузки, поэтому культура по умолчанию - CurrentCulture.
Итак, во-первых, Ordinal будет быстрее (если строка находится близко к началу), верно? А во-вторых, здесь больше логики, которая может замедлить работу (хотя и такая тривиальная)