Содержит быстрее, чем StartsWith?

Консультант приехал вчера, и так или иначе тема строк подошла. Он упомянул, что заметил это для строк меньше, чем определенная длина, 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

Я попробовал его более длинными строками также!

30
задан John Kugelman supports Monica 25 June 2010 в 17:32
поделиться

3 ответа

Попробуйте использовать Секундомер для измерения скорости вместо проверки 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);
}
26
ответ дан 27 November 2019 в 23:40
поделиться

Я разобрался. Это потому, что 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);

    }
}
24
ответ дан 27 November 2019 в 23:40
поделиться

Я покопался в 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 будет быстрее (если строка находится близко к началу), верно? А во-вторых, здесь больше логики, которая может замедлить работу (хотя и такая тривиальная)

3
ответ дан 27 November 2019 в 23:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: