С# Самый быстрый способ сравнить строки

Я заметил, что

string1.Length == string2.Length && string1 == string2

на больших строках немного быстрее, чем просто

string1 == string2

Это правда? И хорошо ли сравнивать большие длины строк перед сравнением фактических строк?

Ответ 1

Оператор

string равен проверке длины перед сравнением символов. Таким образом, вы не сохраняете сравнение содержимого с этим трюком. Вы все равно можете сохранить несколько циклов процессора, потому что ваша проверка длины предполагает, что строки не являются нулевыми, а BCL должен проверить это. Поэтому, если длины не равны в большинстве случаев, вы коротко замыкаете несколько инструкций.

Возможно, я просто ошибаюсь. Может быть, оператор встраивается, и проверки оптимизированы. Кто знает наверняка? (Тот, кто измеряет.)

Если вы заботитесь о сохранении каждого цикла, вы можете, вероятно, использовать, в первую очередь, другую стратегию. Возможно, управляемый код даже не является правильным выбором. Учитывая это, я рекомендую использовать более короткую форму и не использовать дополнительную проверку.

Ответ 2

Оператор String.Equality или == внутренне вызывает string.Equals, поэтому используйте string.Equals или ==, предоставленных каркасом. Он уже оптимизирован.

Сначала сравниваются ссылки, затем длина, а затем фактические символы.

Вы можете найти исходный код здесь

Код: (Источник: http://www.dotnetframework.org/default.aspx/[email protected]/[email protected]/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/clr/src/BCL/System/[email protected]/1305376/[email protected])

// Determines whether two strings match.
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public override bool Equals(Object obj) {
    if (this == null)                        //this is necessary to guard against reverse-pinvokes and
        throw new NullReferenceException();  //other callers who do not use the callvirt instruction

    String str = obj as String;
    if (str == null)
        return false;

    if (Object.ReferenceEquals(this, obj))
        return true;

    return EqualsHelper(this, str);
}

и

[System.Security.SecuritySafeCritical]  // auto-generated
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
private unsafe static bool EqualsHelper(String strA, String strB)
{
    Contract.Requires(strA != null);
    Contract.Requires(strB != null);
    int length = strA.Length;
    if (length != strB.Length) return false;

    fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar)
    {
        char* a = ap;
        char* b = bp;

        // unroll the loop
#if AMD64
        // for AMD64 bit platform we unroll by 12 and
        // check 3 qword at a time. This is less code
        // than the 32 bit case and is shorter
        // pathlength

        while (length >= 12)
        {
            if (*(long*)a     != *(long*)b) break;
            if (*(long*)(a+4) != *(long*)(b+4)) break;
            if (*(long*)(a+8) != *(long*)(b+8)) break;
            a += 12; b += 12; length -= 12;
        }
 #else
        while (length >= 10)
        {
            if (*(int*)a != *(int*)b) break;
            if (*(int*)(a+2) != *(int*)(b+2)) break;
            if (*(int*)(a+4) != *(int*)(b+4)) break;
            if (*(int*)(a+6) != *(int*)(b+6)) break;
            if (*(int*)(a+8) != *(int*)(b+8)) break;
            a += 10; b += 10; length -= 10;
        }
  #endif

        // This depends on the fact that the String objects are
        // always zero terminated and that the terminating zero is not included
        // in the length. For odd string sizes, the last compare will include
        // the zero terminator.
        while (length > 0)
        {
            if (*(int*)a != *(int*)b) break;
            a += 2; b += 2; length -= 2;
        }

        return (length <= 0);
    }
}

Ответ 3

Результаты моих тестов

Сравнить 10000 строк до 10000 других строк одинаковой длины (256)

Время (s1 == s2): 32536889 тиков

Время (s1.Length == s2.Length) && & & (s1 == s2): 37380529 тиков

Сравнить 10000 строк до 10000 других строк Случайная длина макс 256

Время (s1 == s2): 27223517 ticks

Время (s1.Length == s2.Length) && & & (s1 == s2): 23419529 тиков

Сравнить 10000 строк до 10000 других строк Случайная длина min 256 max 512

Время (s1 == s2): 28904898 тиков

Время (s1.Length == s2.Length) && & & (s1 == s2): 25442710 тиков

То, что я считаю ошеломляющим, состоит в том, что сравнение 10000 строк равной длины займет больше времени, чем сравнение того же объема данных, который больше.

Все эти тесты были выполнены с точно такими же данными.

Ответ 4

Согласно ILSpy, оператор строки == определяется как:

public static bool operator ==(string a, string b)
{
    return string.Equals(a, b);
}

Что определяется как

public static bool Equals(string a, string b)
{
    return a == b || (a != null && b != null && a.Length == b.Length && string.EqualsHelper(a, b));
}

Я предполагаю, что первая a == b на самом деле является контрольной проверкой равенства (ILSpy просто показывает ее как ==), иначе это будет бесконечно рекурсивный метод.

Это означает, что == уже проверяет длины строк перед фактическим сравнением своих символов.

Ответ 5

В завершенных строках имеет смысл просто начинать сравнивать символы, так как вы не можете рассчитать длину строк без итерации всех символов в любом случае, и сравнение, вероятно, приведет к скорейшему завершению.

С подсчитанными по длине строками, сначала следует выполнить сравнение длины, , если вы проверяете байт-правильное равенство. Вы даже не можете начать доступ к символьным данным без извлечения длины, так как может быть нулевой длины.

Если вы делаете реляционное сравнение, знание разных значений не говорит вам, должен ли результат быть положительным или отрицательным. И в сопоставлении с культурой одинаковые строки не подразумевают равную длину. Поэтому для обоих из них вам нужно просто сравнить данные.

Если operator==(string, string) просто делегирует реляционное сравнение, вы не ожидаете, что сравниваете длины. Поэтому проверка длины перед проведением сравнения может быть выгодной. Но похоже, что Framework начинается с проверки длины.

Ответ 6

Для geeks среди нас здесь страница, которая отлично справляется с бенчмаркингом многочисленных способов сравнения строк.

В двух словах самым быстрым методом является CompareOrdinal:

if (string.CompareOrdinal(stringsWeWantToSeeIfMatches[x], stringsWeAreComparingAgainst[x]) == 0)
{
//they're equal
}

Вторым лучшим способом, по-видимому, является либо словарь, либо Hashset с "ключом" в качестве строки, которую вы хотите сравнить.

Делает интересное чтение.

Ответ 7

Я бы сказал, что первый из них быстрее, а результат string1.Length == string2.Length - false. Благодаря оценке коротких замыканий (SCE) фактическое сравнение строк не производится, что может сэкономить ваше время.

Если строки равны, то первый из них медленнее, так как сначала проверяет длину, а затем выполняет то же самое, что и второе.

См. http://msdn.microsoft.com/en-us/library/2a723cdk.aspx для информации о && и SCE.

Ответ 8

Итак, как я и обещал, я написал короткий код с секундомером - вы можете скопировать его и попробовать разные строки и увидеть различия

class Program
{
    static void Main(string[] args)
    {
        string str1 = "put the first value";
        string str2 = "put the second value";
        CompareTwoStringsWithStopWatch(str1, str2); //Print the results.
    }

    private static void CompareTwoStringsWithStopWatch(string str1, string str2)
    {
        Stopwatch stopwatch = new Stopwatch();

        stopwatch.Start();
        for (int i = 0; i < 99999999; i++)
        {
            if (str1.Length == str2.Length && str1 == str2)
            {
                SomeOperation();
            }
        }
        stopwatch.Stop();

        Console.WriteLine("{0}. Time: {1}", "Result for: str1.Length == str2.Length && str1 == str2", stopwatch.Elapsed);
        stopwatch.Reset();

        stopwatch.Start();
        for (int i = 0; i < 99999999; i++)
        {
            if (str1 == str2)
            {
                SomeOperation();
            }
        }
        stopwatch.Stop();

        Console.WriteLine("{0}. Time: {1}", "Result for: str1 == str2", stopwatch.Elapsed);
    }

    private static int SomeOperation()
    {
        var value = 500;
        value += 5;

        return value - 300;
    }
}

Мои выводы:

  • Когда я проверил несколько строк (короткие и длинные), я увидел, что все результаты почти одинаковы. Итак, первое, если (с проверкой длины) медленнее в 2/3.
  • И у вас есть метод Equals в классе Object, просто используйте его:)
  • Вы можете попробовать и дать нам результаты:)

Ответ 9

Если вы ожидаете, что строки будут отличаться по длине в большинстве случаев, вы можете сравнить их длины И, а затем сравнить сами строки, используя string.Compare. Сделав это:

if (str1.Length == str2.Length)
{
    if (string.Compare(str1, str2, StringComparison.Ordinal) == 0)
    {
       doSomething()
    }
}

В этом случае я ожидаю, что строки будут отличаться почти все время, я думаю, str1.Lenght намного дешевле, чем сравнение фактических строк. Если они равны по размеру, я их сравниваю.

ИЗМЕНИТЬ: Забудьте, что я сказал. Просто используйте == и будьте счастливы.